Neil Bothwick goes back to the start of Linux, even before root existed
When you boot your computer, the bootloader only needs two pieces of information: the path to the kernel and the location of your root filesystem, which is then passed to the kernel. In Grub, this would look something like:
linux /boot/vmlinux-3.19.0 root=/dev/sda1
In practice, you will see a lot more in your bootloader entries. Anything on the Linux line after the path to the kernel file is a set of arguments that are passed on to the kernel and used by the Linux boot process, but you will usually see an extra line that starts with initrd followed by a path to another file in /boot, like:
initrd /boot/initramfs-3.19.0.img
In the early days of Linux, the initrd option was not used. The kernel itself and the root filesystem contained everything needed to start the boot process and mount any other filesystems before they were needed. That changed as the amount of hardware supported by Linux increased, although it’s still possible on custom systems. The kernel needs to be able to mount the root filesystem, which means it must contain drivers for your motherboard’s disk interface and the filesystem used for root. As the hardware permutations increased, it became impractical and then impossible to build everything into the kernel, so the idea of an initial ramdisk was born.
The initrd and its successor, the initramfs (they differ in technical implementation rather than usage) is a temporary root partition that’s loaded into memory (hence the ramdisk name) to set up and mount the real root filesystem before passing control to it. This means you can have a ramfs that contains drivers for a variety of hardware — look in /boot and you will see the ramfs file is several times the size of the kernel. Very soon, distro makers switched to using a ramfs to boot their kernels, to support as much hardware as possible, and now we have other things to consider, such as encrypted filesystems or filesystems on logical volumes — or both.
ROLL YOUR OWN
You can make your own initramfs, it’s a simple CPIO archive with an executable script called init in its root and the files needed to run that script, most of which are provided by busybox. You don’t even need to build the ramfs yourself, it can be done during kernel compilation by setting CONFIG_INITRAMFS_SOURCE to the path to a configuration file (basically a contents list) in the kernel. If you are trying to build a minimal system with a pareddown kernel but still need an initial ramfs, this is the approach to take. It is well documented in the kernel documentation at /usr/src/linux/Documentation/filesystems/ramfs-rootfs-initramfs.txt. But that’s a bit hard core for this tutorial. What do you do if you need to compile your own kernel, maybe to add support for esoteric or new hardware, and need to generate an accompanying ramfs? Enter Dracut [stage left, wearing a velvet cloak].
In the past each distro had a custom script called something like mkinitrd but nowadays the tool of choice is Dracut. Using udev, and systemd if it is available, Dracut builds a ramfs that can be more or less universal. The tool is included in most distros’ repositories and after installing you can generate a ramfs very easily by running:
dracut
When run without any arguments, Dracut generates an initramfs for the currently running kernel, called /boot/initramfs-KERNEL-VERSION.img. This fits in with the naming scheme expected by Grub, so grub-mkconfig will generate the correct menu entry for it. You can change the name by specifying it on the command line. The first non-option argument is the name of the initramfs file:
dracut newinitramfs.img
Unlike the distro-specific ramdisk builders used in the past, a Dracut initramfs can be universal. If you change your hardware or put your disk in a different computer, it should still work. This is because very little is hard coded, it uses udev to detect your hardware and then acts accordingly. This is the approach taken with distro releases, because they need to support as much hardware as possible, but if you are building a new initramfs for your computer, you only need to include what’s needed for your computer. You do this by adding the --hostonly option to Dracut and it results in a substantially smaller — but not portable — file.
You may have noticed the reference to Dracut building a ramfs for the currently running kernel, which is fine if you just want a new one ramfs, but what if you have a new kernel, you need to build the ramfs before you can boot it, which you do with the --kver option:
dracut --hostonly --kver 3.19.0-custom
MORE CHOICES
Dracut uses modules, not to be confused with kernel modules, to perform the various tasks when starting the system. There is a default set used, and listed when you run Dracut, but you can add or remove modules with the --add and --omit options, which each take a spaceseparated list of modules, enclosed in quotes:
dracut --omit “dmraid mdraid lvm”
Typing all these options each time can be error prone, not to mention tedious, so you can place most of them in a file in /dev/dracut.conf.d. The name is unimportant except it must end in .conf, for example:
hostonly=”yes”
omit_dracutmodules+=”mdraid caps i18n”
omit_drivers+=”vboxdrv vboxnetadp vboxnetflt vboxpci”
The third line excludes kernel modules, You are hardly likely to run VirtualBox from the ramfs. Once you’ve created your ramfs, you may want to see what’s in it by using
lsinitrd /boot/initramfs-3.19.0-custom.img
which lists the contents of the given image, or the one for the current kernel if you don’t give an image file. You can also inspect the contents of individual files with the -f option:
lsinitrd /boot/initramfs-3.19.0-custom.img -f etc/modprobe.d/aliases.conf
BOOT OPTIONS
Running with a Dracut-built initramfs may require some changes to your boot options, especially if you weren’t using a Dracut initramfs before. If you have built an initramfs for your currently running kernel, you can see the kernel options to use by running:
dracut --print-cmdline
You can then add the options, apart from the root setting as that is already covered by Grub, to GRUB_CMDLINE_LINUX in /etc/default/grub. Dracut, like Grub, defaults to using UUIDs to identify the root partition. These are portable but not particularly readable. You can still use the old style root=/dev/sda3 notation, or give the filesystem a label and use root=LABEL=mylabel. Dracut adds a number of extra boot options you can use, mostly beginning with rd. Some of them are quite specialist, but there are some general ones. Two that are useful when debugging a new setup are:
rd.shell Drop to a rescue shell if booting fails.
rd.debug Output extra information, both to the screen and to the file /run/initramfs/rdsosreport.txt.
There are also options to control how the initramfs looks for devices to boot from. Adding rd.auto=1 turns on looking for special devices, like encrypted, LVM or RAID. There are also specific switches for LVM and RAID: rd.lvm, rd.mdraid, rd.dmraid. All of these switches will need to be set to 1 to enable or 0 to disable.
When running dracut --printcmdline, you will have seen rootflags and rootfstype, specifying mount details for the root fi lesystem. If you prefer, you can omit these and they will be read from /etc/fstab, provided you have given enough information to mount it in the first place. Once you have your configuration set up, generating a new initramfs is a simple matter of running Dracut, Exploring the use of Dracut is good for those that use a custom kernel or just want to know how things work.
What if it doesn’t work?
The first thing to do when trying to build a new initramfs is to make a backup copy of the working one in /boot, by changing the extension from .img to .bak. Then if things go wrong you can press e key at the Grub menu and switch to the backup file.
If you add the rd.debug and rd.shell boot options, the initramfs will drop you into a rescue shell if things go wrong. Here you can try to diagnose the problem. The file /run/initramfs/rdsosreport.txt will have a record of the boot attempt. You should be able to mount your boot partition from the rescue shell and copy the file there, enabling you to reboot into your working option and browse the file. If you are using Systemd, the journal should also be available, read it with:
journalctl -ab
You may also find the rd.break option useful when debugging. Given alone, it drops to a shell when the initramfs would normally switch to the real root filesystem, but you can also give it a specific breakpoint at which to stop the boot process and drop to a shell, so you can see just what’s going on. The breakpoints you can give are cmdline, pre-udev, pre-trigger, initqueue, pre-mount, mount, pre-pivot and cleanup.
If you built your initramfs with the hostonly option, try building a generic one and compare how that works.