Creating purpose-built TinyCoreLinux Images
Why would you want to do that?
Things you could do include
- Creating minimal images for specialized computers e.g. for
- Signage Displays
- POS Terminals
- Remote Cameras
- Twitter walls
- Creating custom built live systems e.g. for
- Teaching in a bring-your-own-device setting
- Projects requiring a special environment
- Privacy-sensitive applications such as online banking
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
- A TinyCore CDROM image which you can download from tinycorelinux.net
- QEMU for easy testing
- Tools to interact with partitions and file systems, such as fdisk and e2fsprogs. Those were probably already installed by your distribution
- GRUB2 for use as bootloader
Preparations
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
Partitioning
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
- /media/image
- /media/image/boot
- /media/image/tce
- /media/image/tce/optional
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
boot
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
tc@box:~$
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
tc@box:~$
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, filetool.sh
(there’s also a graphical interface
called filetool
). filetool.sh
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
filetool.sh -b
The X11-enabled distributions of TinyCore actually include a prompt at shutdown, which defaults to running filetool.sh -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.
Feedback
Liked this article? Got some questions or something smart to add? Drop me a line at fjs@fabianstumpf.de!