Creating purpose-built TinyCoreLinux Images

Why would you want to do that?

Things you could do include

If you’ve done cool stuff with the help of this article, tell me about it!

What’s TinyCore?

TinyCore is a very minimal distribution of Linux, featuring just a Kernel and an initial ramfs. It fits on almost anything (eg. a puny 64MB CF storage card), while still being extendable and able to run almost anything you’re able to compile on Linux.

What you’ll need


All of this can also be done with a real disk (such as a CF card), but I’ll use QEMU images since that allows rapid testing of images.

Creating a QEMU disk image

Create a new QEMU image with

qemu-img create -f qcow2 core-image.img 64M

This will create core-image.img as a qcow2-formatted QEMU image with a maximum size of 64 megabytes. The qcow2 format enables the image to grow dynamically (as in, the file will only take up as much space as the data it contains).

Connecting the image as block device

Mounting QEMU images is done via NBD (network block devices). To use that, you’ll first need to load the kernel module providing nbd.

As root, run

modprobe nbd max_part=8

You should now have some new block devices under /dev, probably named something like /dev/nbd[0-9], to which you can now connect the image by running

qemu-nbd -c /dev/nbd0 core-image.img

Keep in mind this is still only the block device, not yet containing a mountable file system.

Mounting the TinyCore image

In order to copy all the data necessary to boot a minimal TinyCore, mount the downloaded image to /media/cdrom/ by running

mount Core-current.iso /media/cdrom

This allows you to browse the image just like a normal file system.

Preparing the disk


In order to use the disk, you may have to configure it to use a partition layout suited to booting TinyCore. In the case of a fresh QEMU image, the disk is completely empty. To fix that, run

fdisk /dev/nbd0

to bring up the fdisk disk partitioner. fdisk commands are very short (as in, they’re mostly single characters), but fortunately you’re provided with the helpful hint that pressing m followed by Enter brings up an overview of what you can do.

Press p and hit Enter to see a list of the current partitions on the disk. You’ll need at least one partition of at least 25MB, while leaving some space before the first partition for grub2’s core.img. If you want to start fresh with a disk, delete all partitions with d.

Hit n and Enter to create a new partition on the disk. Choose p for a primary partition and, if you’re starting fresh, 1 for the partition number.

Grub will copy its core.img into the sectors before the first partition, which normally should be taken into account by fdisks default (in my tests, it was almost always 2048). Accept the default for the last sector of the partition to have it span the rest of the disk or enter something else if you know what you’re doing ;)

The last thing you’ll need to do in fdisk is making the new partition bootable, which is easily done by hitting a and Enter.

After these steps, p should tell you something like

/dev/nbd0p1 *     2048 131071  129024  63M 83 Linux

which basically means that all of your actions resulted in a new bootable partition on the device ;)

Now hit w and Enter to write the changes to the block device. fdisk will automatically exit after this.

You should now have a new device node for the partition, something like /dev/nbd0p1

Formatting the new partition

Now that you created the basic partition layout on the device, you’ll need to create a file system on it. One of the most widely-supported file systems (at least in the Linux world) is ext2. Format the new partition to ext2 by running

mkfs.ext2 /dev/nbd0p1

This will take some seconds to create the basic accounting information needed for operation of the file system.

Mounting the new partition

In order to copy the necessary files over to the disk, mount it by running

mount /dev/nbd0p1 /media/image

You may need to create the mountpoint first. Within /media/image you’ll now find a folder called lost+found which is a remnant of the formatting step and can be safely deleted.

Creating the basic directory structure

In this step, you’re going to create the basic directories needed for booting TinyCore.

First create a directory named boot. This directory is going to contain data needed by GRUB to start the image as well as the kernel and the initial ramfs.

Next, create a directory named tce and within that, create another directory named optional. The tce directory will later contain information about TinyCore extensions to be loaded, while the optional directory will contain the actual extension files.

The final directory layout should look like

Installing TinyCore files

From the cdroms boot directory, copy the files core.gz and vmlinuz to the boot directory of your new image. These are the files that actually make up TinyCore, the kernel and the initial ramfs.

Create a file named onboot.lst in /media/image/tce. This file will contain a line-by-line listing of the modules in /media/image/tce/optional that are to be loaded on boot.

Installing GRUB

Next, you’re going to install the boot loader, GRUB2. This step is pretty easy as it really only involves one command.

grub-install --boot-directory=/media/image/boot /dev/nbd0

This installs GRUB to the disk /dev/nbd0 and copies its additional files to /media/image/boot/grub.

Booting the image

This step is optional, but it may teach you something about operating the GRUB command line ;) To be on the safe side, unmount the image with umount /dev/nbd0p1 first and disconnect the network block device using qemu-nbd -d /dev/nbd0.

If you’re using QEMU, you can spin up a virtual machine running the new image by running

qemu-system-i386 core-image.img

Alternatively, if you’re testing on a bare metal system, grab the storage medium and boot the system from it.

Since GRUB is not yet configured properly, you’ll be greeted with its command line, which knows some basic commands like ls.

In order to boot, GRUB needs to know where to find a kernel to execute and a ramdisk to load. If you followed these instructions, those are most probably located on (hd0,msdos1) (the first partition on the disk), in the boot directory you created earlier. Inform GRUB of this by entering

linux (hd0,msdos1)/boot/vmlinuz
initrd (hd0,msdos1)/boot/core.gz

Now continue booting by issuing the command


The kernel will now spit out a lot of debugging information while starting up, some if which can be helpful when trying to find out what went wrong with an image. Finally, after the system is done booting, you will be presented with the default TinyCore prompt


You can now explore the system (though there is not that much to do yet), but be aware that any changes you make are going to be lost upon the next reboot, due to the way TinyCore handles persistence. I’ll come back to that later.

Once you’re done, shut down the VM with

sudo poweroff

Configuring GRUB

In the last step (if you chose to do it), you booted the system by issuing commands at the GRUB commandline. This is obviously not the most desirable way to start a system, so in this step, you’ll create a simple menu to choose pre-defined boot options from.

First, mount the image back to /media/image in order to be able to edit it some more. GRUB takes its default config from $(root)/boot/grub/grub.cfg, where $(root) is the bootable partition.

Create the file at /media/image/boot/grub/grub.cfg and edit it. Since this article is not about the syntax of GRUB configuration files, I’m going to stick to the basics.

Probably the simplest configuration file that is useful looks like this

set timeout=3
set root='hd0,msdos1'
menuentry 'Boot TinyCore' {
    linux /boot/vmlinuz
    initrd /boot/core.gz

File name: /media/image/boot/grub/grub.cfg

If you want to pass the boot process some arguments, append them after the path to the linux kernel. TinyCore supports a bunch of those, here are some of the more useful:

Parameter Usage Example Effect
waitusb waitusb=5 Wait for USB devices to become ready before mounting. This might help if the image fails to load its extensions.
norestore norestore Do not restore from persistence file (explained later in this guide)
lst lst=ext.lst Load extensions specified by ext.lst
superuser superuser Start with a root shell
quiet quiet Suppress much of the debug information during boot

Booting the image (again)

After the last step, you’re finally ready to boot into TinyCore productively. Unmount the image with

umount /dev/nbd0p1

and disconnect the network block device with

qemu-nbd -d /dev/nbd0

Now you can boot from the medium, or when using QEMU, spin up a VM by running

qemu-system-i386 core-image.img

Instead of the command line, GRUB should now present you with a menu containing a single item and a timeout of 3 seconds.

After a lot of kernel information, you will (once again) be presented with the default TinyCore prompt


Installing some packages

Now that the base image is running, you can install additional software to specialize the system. TinyCore includes a basic package manager called tce (TinyCore Extensions).

Start the interface by running tce and type s to enter search mode. Enter the name of the software you want to install and hit enter. Once something was found, type the number of the result you want installed, read the package notes, hit q to exit the pager and i to install the package.

Keep in mind that all changes you currently make to configuration files are not yet persisted across reboots. I’ll come back to that later.

Adding your own packages

TinyCore packages are really just squashfs files being mounted to / at boot time, so they’re easily downloaded from the web. In order to install a package from a .tcz file, just copy it to the /tce/optional/ directory of your image and add the filename to /tce/onboot.lst to have it loaded at boot time.

This is pretty much exactly what tce does in the background.

Set up persistence

As already mentioned, TinyCore packages are really just squashfs files being mounted to / at boot time. This also means that they do not contain any dynamic data, but will restore their exact same state on every boot.

In some cases, this might be beneficial (as it effectively resets the system each reboot), but most of the time, you’ll want to be able to save things across reboots. This is what persistence means in the context of live systems.

TinyCores persistence mechanism is a little tool called, fittingly, (there’s also a graphical interface called filetool). creates a gzipped tar archive of all files/paths from /opt/.filetool.lst while excluding all paths from /opt/.xfiletool.lst and writes it to the disk containing the TinyCore installation as mydata.tgz. Data from mydata.tgz is read after all packages have been mounted and is unpacked over the existing files, making it possible to also include configuration files in the persistence file. By default, /opt/.filetool.lst contains only /opt/ and /home/.

You can manually invoke a backup of all data to be persisted by running -b

The X11-enabled distributions of TinyCore actually include a prompt at shutdown, which defaults to running -b before terminating.

Burning to disk

Now that you’ve configured the image to your liking, you can either copy it to other disks of the same size with dd like

dd if=/dev/nbd0 of=/dev/sdc bs=4M; sync

or repeat the formatting steps from the preparations with the new medium and simply copy the entire contents of the image to it (this is faster with images that mostly contain blank space). Note that after copying, you still need to install GRUB to the new medium. When using the dd method, a 1:1 copy is created, including the GRUB bootloader.

Tips and Tricks

If you want your image to boot to a graphical environment, download the TinyCore image with X11-support and simply copy all the extensions in its /tce/optional folder to your image.


Liked this article? Got some questions or something smart to add? Drop me a line at!