Custom Initramfs

initramfs is a root filesystem that is embedded into the kernel and loaded at an early stage of the boot process. It is Article description::the successor of initrd. It provides early userspace which can do things the kernel can't easily do by itself during the boot process.

Using initramfs is optional. By default, the kernel initializes hardware using built-in drivers, mounts the specified root partition, loads the init system of the installed Linux distribution. The init system then loads additional modules and starts services until it eventually presents a log in dialog. This is a good default behavior and sufficient for many users. initramfs is for users with advanced requirements; for users who need to do things as early as possible, even before the root partition is mounted.

Typically, an initramfs is not needed, but may be necessary for:


 * Mounting an encrypted, logical, or otherwise special root partition
 * Providing a minimalistic rescue shell (if something goes wrong)
 * Customize the boot process (e.g., print a welcome message)
 * Load modules necessary to boot (e.g., third party storage drivers)
 * Anything the kernel can't do that's usually handled in user space

Prerequisites
There are countless ways to make an initramfs. An initramfs need not be manually created, there are tools, such as Genkernel or Dracut, that can do the work. With a bit of luck, one of them might work out of the box for the required use-case, and the user need not bother with how initramfs works and what it does. If none of these tools can do the job automatically, their functionality may have to be extended manually, or initramfs could have to be "hand made".

An initramfs contains at least one file called. This file is executed by the kernel as the main init process (PID 1). It has to do all the work. In addition, there can be any number of additional files and directories that are required by. They are usually files often found on any other root filesystem, such as for device nodes,  for kernel information,  for binaries, and so on. The structure of an initramfs can be simple, or it can be complicated, depending on use-case requiremnts.

When the kernel mounts the initramfs, the target root partition is not yet mounted, so it can't access any of the files there. That means there is nothing but the initramfs. So everything required must be included in the initramfs. If a shell is required, it must be included in the initramfs. To be able to mount something, a mount utility must be included. To load a module, the initramfs has to provide both the module, as well as a utility to load it. If the utility depends on libraries in order to work, the libraries must be included as well. This seems complicated, and it is, because the initramfs has to function independently.

Basics
This section will show the easy and straightforward way to initramfs creation, to make a functional - albeit minimalistic - initramfs which then can extend according to needs..

Directory structure
Create a basic initramfs directory structure that will later become the initramfs root. For consistency work in, but any location would do. Adapt accordingly.

Device nodes
Most things the initramfs does will require a couple of device nodes to be present, especially the device for the root partition. Throughout this document, will be used as example device. Copy basic device nodes from the root filesystem to the initramfs example location:

Which devices are needed depends entirely on what the system is going to use initramfs for. Please adapt to the system needs.

Applications
Any binary needed to execute at boot needs to be copied into the initramfs layout. Make sure to also need to copy any libraries that the binaries require. To see what libraries any particular binary requires, use the tool. For example, the binary requires:

Here shows that for to work in the initramfs,  In this example,  to, but also  and  to. The exception is which is provided by the kernel.

Some applications might depend on other files and libraries to work. For example, needs a terminfo file  from, so copy it to the initramfs as well. To find these dependencies, tools like and  prove to be most helpful.

Busybox
Instead of collecting countless utilities and libraries (and never seeing the end of it), just use busybox. It's a set of utilities for rescue and embedded systems, it contains a shell, utilities like, , , , , and many more - all in a single binary called. For to work properly in a initramfs, emerge it with the   USE flag enabled, then copy the  binary into the initramfs layout as :

Set the useflags  and   to /etc/portage/package.use/busybox

Init
The file structure the initramfs is almost complete. The only thing that is missing is itself, the executable in the root of the initramfs that is executed by the kernel. Because includes a fully functional shell, this means that the  binary can be written as a simple shell script (instead of making it a complicated application written in Assembler or C that needs to compile).

The following example shows a minimalistic shell script, based on the shell:

This example needs some device nodes to work, mainly the root block device. Change the script and copy the corresponding node to fit the system needs.

Don't forget to make the file executable:

Packaging
The initramfs now has to be made available to the kernel at boot time. This is done by packaging it as a compressed archive. This archive is then either embedded directly into the kernel image, or stored as a separate file which can be loaded by the bootloader during the boot process. Both methods perform equally well/

Kernel configuration
With either method, there is a need to enable Initial RAM filesystem and RAM disk (initramfs/initrd) support.

Embedding into the Kernel
The initramfs to be embedded into the kernel image, set Initramfs source file(s) to the root of the initramfs, (e.g. ) but this isn't necessary:

Now compile the kernel it will automatically put the files into a cpio archive and embed it into the kernel image. There will need to rebuild the kernel any time a changes is made to the initramfs.

Creating a separate file
To use a standalone archive file, adjust the kernel settings accordingly:

For this example is sufficient.

Create a standalone archive file by running the following commands:

This will create a file called in. Now instruct the bootloader to load this file along with the kernel.

Using GRUB
In case of GRUB, do this with the initrd line:

In order to make this usable with, the filename must be included in the GRUB helper scripts:

After applying the changes, the file will be recognized running ; the output may look like the following:

External file list
An external file list, or cpio list, describes files to be included into the initramfs. This file list is processed by an utility that comes with the Linux kernel,. It can be used for both embedded and standalone initramfs, either by using it as directly or by running the utility from a shell. This lets the initramfs be built dynamically, always using the latest files from the system, but compared to populating a real directory, it is less intuitive and requires more knowledge in regards to device nodes and such. The command will prove as very useful to get information from special block or character devices.

A minimalistic list may look like so:

Create the cpio archive, compress it and move it to :

After that update the bootloader entries!

Instead of creating the list manually, there is the possibility to let the cpio list and archive be generated by two tools, which are provided by the kernel source files. It might be needed to compile these first; this is documented here.

Using the previously created directory structure in, it may be executed like the following:

The script creates a cpio list by analyzing all directories and files within. It then executes to generate the cpio archive file.

After that, compress the via :

This creates the archive.

In order to comprehend, all steps can be done manually:

The file can now be moved to.

Finalizing
Now reboot the machine. On boot, the kernel will extract the files from the initramfs archive automatically and execute the script, which in turn should then take care of mounting of the root partition and execute the init system of the installed Linux distribution.

Functionality
To extend the script with more advanced functionality.

Rescue shell
To be dropped to a rescue shell if an error occurs, add the following function to and call it when something goes wrong.

In the example below, the rescue_shell will be executed if the root partition fails to mount:

Force entry into the rescue shell
Occasionally, it might be useful to be able to interrupt the boot process and enter the rescue shell. For example, it's useful for root password (or PAM configuration) recovery or for debugging itself.

A standard way of doing this is by passing  or   kernel command line option. However, this is a rarely known option. It also requires /bin/sh or /bin/bb symlinks/files to be built into initramfs instead of being installed at runtime with. It's possible to call it like  but this is even more obscure.

For convenience, it might be useful to implement Dracut-inspired  option used for a similar purpose (legacy name was  ). Similarly, it's possible to handle more widely known  option in the same manner.

Dynamic devices
For populating dynamically, use either devtmpfs or mdev. Please note that the kernel can take some time detecting devices (such as external USB drives), so a sleep statement may need adding to the script.

devtmpfs
Provided by the kernel, devtmpfs is designed to offer device nodes during early boot.

Include the following snippet in the script to have it mount at boot:

Don't forget to unmount it again in the cleanup phase of the script:

mdev
Although devtmpfs is the preferred solution today, alternatively use mdev, the udev replacement of.

For mdev to work, make a symlink to  in the initramfs.

Then add the following snippet to, after mounting  and :

Mount by UUID or label
With Dynamic devices enabled, it may be preferable to use UUID or label to mount the root partition instead of using a static device name. For that purpose, comes with a utility called.

Doing it this way is simple, but it means that the UUID or label is hardcoded in the. Alternatively, see Kernel parameters.

Kernel parameters
Using kernel parameters instead of hardcoding device names or UUIDs, there will be a need to parse. There are many ways to do so, the following method is just an example to give the general idea. It uses string manipulation of the shell and only supports  parameters. Add the following function to and call it whenever kernel parameter is needed.

The function is called with the name of the kernel parameter in question. In the example below, it uses the root parameter to mount the root partition:

It works for both  and   but will fail when the parameter is missing.

LVM
If the root partition is located on a logical volume, include the LVM binary in the initramfs. Get a static binary by enabling the  USE flag for. Copy it to the initramfs directory.

Now, enable the LVM root partition in. This example assumes that the volume group is called VG, and the root volume is called root. Replace them with the names the system used when creating the volume.

The root partition may then be called or.

Recent versions of rely on  to create the named LV device nodes, but there is no udev in a simple initramfs. The following choices are available:


 * Use as shown above (simplest solution)
 * Mount by UUID or label instead of using . It works because is happy with just
 * Build a LVM binary with the  USE flag (specifically for the initramfs only!)
 * Disable udev dependency by including a minimal in the initramfs:

Software RAID
Normally the Linux kernel will automatically scan for any "Linux raid autodetect" partitions and start as many software RAIDs as it can find. But if using an initramfs, the kernel will not automatically scan for RAIDs until it is told to. In the following example instructs the kernel to scan for software RAIDs and start as many as it can find. This will actually start all autodetected arrays, not just :

mdadm
Without "Linux raid autodetect" partitions, or if an advanced RAID setup is required, include mdadm in the initramfs. A static binary may be made by enabling the  USE flag for.

Copy the binary and  into the initramfs:

Edit the in the initramfs as required. An example follows:

This will scan all  devices and assemble the RAID device fitting the UUID 627125a5:abce6b82:6c738e49:50adadae.

Now Software RAID can be initialized in :

With this, the root partition should be able to be mounted.

DM-Crypt
If the root partition is LUKS encrypted, include the cryptsetup binary in the initramfs. A static binary may be made by setting the  USE flag for. Copy it to the initramfs directory. Since cryptsetup also often requires the use of the kernel's random device, include them as well.

Recompile the package with the new USE flags. For example:

It might also be needed to compile the package with the   USE flag:

After that copy over the binary files:

Now it is possible to unlock the encrypted root partition in :

Once the passphrase is entered, the root partition will be available as.

Encrypted keyfile
If encrypted keyfiles are required, use to encrypt them. It keeps the initramfs simple as that's the encryption tool already present - no need to add other binaries. Plus, unlike some of the alternatives, it offers a nice password prompt.

The following example creates a random 512 byte key, encrypted with LUKS, and adds it to the LUKS container.

Unlocking the root device using this key in the can then be done like this:

As before, the root partition should then be available as.

Networking
If networking is required in initramfs, all required network related drivers have to be built into the kernel, and the network interfaces must be configured in. How exactly this has to be done, depends on the network situation. The following sections cover only the most common cases.

Static IP
If the network situation allows the use of a static network IP, it is possible to set it up using the and  commands, both of which are included in Busybox. This is by far the easiest solution, so if it's at all possible, go for it.

DHCP
To obtain a dynamic IP address from the network's DHCP server, a DHCP client is required. Busybox comes with a minimalistic DHCP client called, which is sufficient for most users. Unfortunately, has a dependency: it requires the help of a separate script to actually configure the network interface. An example for such a script is included in the Busybox distribution, but it's not installed by Gentoo. It must be obtained directly from the Busybox tarball (it's called ) or download it from the Busybox project page.

Copy the script to the initramfs and make it executable.

Edit the script's first line to read #!/bin/busybox sh or create a symlink for :

Now, it's possible to obtain a dynamic IP address for eth0 using DHCP:

DNS
The network should be up and running now. However, that's only if exactly which IPs to talk to is known. If only a host or domain name is known, it's a different story entirely. In that case, it will be required to be able to resolve hostnames. Unfortunately, this is where our luck leaves us. Until now, everything could be done with just the static binary of Busybox - however, this is not the case with DNS.

This is because itself dynamically includes additional libraries for DNS lookups. As long as that functionality is not required, everything should work, but if it is needed, there is no choice but to include those libraries in the initramfs. The only alternative would be building Busybox against another libc such as, however that would go beyond the scope of this article.

This is a good chance to demonstrate how to use to reveal hidden dependencies.

The command accesses quite a lot of files, some of which are mandatory for it to work.

Copy the necessary libraries to the initramfs:

Create a with at least one useable nameserver. Note that this step may be done automatically if using DHCP.

With this, DNS lookups should now work.

Custom keyboard layout
Busybox provides loadkmap to set keyboard layout. Since it only accepts binary keymaps, they must be converted first.

Clean up:

Now just update the init-script:

Troubleshooting
The following section tries to provide help with common issues and pitfalls.

Static vs. dynamic binaries
Any custom binaries needed to be used in the initramfs before mounting have to be fully functional, independent from any files installed on the root partition. This is much easier to achieve with static binaries (which usually work as single file) than with dynamic binaries (which need any number of additional libraries to work).

Gentoo provides static binaries for some ebuilds. Check if the ebuild for the binary offers a  or   USE flag. This is by far the easiest method to get a static binary, but unfortunately only a select few ebuilds support it.

Many applications also offer static builds with an option in their configure scripts. There is no standard name for the option, it may be  or something similar. When compiling a package manually, check the list of available options by using to see if the package supports building static binaries.

It is possible to check whether or not a binary is static by using the command. The command is also very useful to find out about additional dependencies. By using it is possible to see which files a certain package has brought into the system, some of which may also be candidates for additional dependencies of that package.

Including libraries into the initramfs in order to make a dynamic executable work is a last resort only. It makes the initramfs much larger than necessary and harder to maintain, as the dependencies might change with every update of the program in question.

lddtree
If deciding to go with dynamic binaries, comes with a Python script  which will handle the collection of libraries:

That will copy the binary and all its libraries to the initramfs structure - but not any of the runtime dependencies. For more details refer to.

Kernel panics
When working with initramfs and writing custom init scripts for it, the following kernel panic may be encountered on boot:

Kernel panic - not syncing: Attempted to kill init!

This is not an error in the kernel, but an error in the script. This script is executed as the init process with PID 1. Unlike other processes, the PID 1 init process is special. It is the only process that is started by the kernel on boot. It's the process that takes care of starting other processes (boot process, init scripts) which in turn start other processes (daemons, login prompts, X), which in turn start other processes (bash, window manager, browser, ...). The init process is the mother of all other processes, and therefore it mustn't be killed. On shutdown, it's again the init process that takes care of cleaning up by shutting down other processes first, then running processes that will unmount the filesystems, until it is safe to actually do a shutdown without corrupting anything.

If there is some error in the script, that causes the init process to end, this basically means there are no processes left to run, there is nothing that could take care of cleaning up, and the kernel has no choice but to panic. For this reason there are some things in that can't be done like in a normal shell script, like using return or exit, or letting the script just run a series of commands and then simply end.

If should end, pass the responsibility of the init process to another process using exec. See the examples above how exec is used to either run /sbin/init of the mounted root partition or to run a rescue shell in case something went wrong.

Job control
While working with initramfs, especially the Rescue shell, this message mya be encountered:

/bin/sh: can't access tty; job control turned off

The lack of job control is usually not a problem, since is not supposed to be interactive. However, to work with the Busybox shell on a regular basis, being unable to control programs with + or + can easily become a huge issue. In worst case, if job control is not available, and a program refuses to quit, reboot.

The job control section in the Busybox FAQ offers some help here. Either use:

or:

to start a shell on tty1 with job control enabled.

Salvaging
If for whatever reason the structure was lost, but either the kernel image with the built-in initramfs, or the separate cpio archive is still available, it's possible to salvage it from there. Although it may be easier to just redo it from scratch - after the first try, doing it again should be a piece of cake. So this is just in case.

Dismantling the Kernel
Skip this step if the initramfs is a separate cpio archive already. Otherwise, it will be required to get the built-in cpio archive out of the kernel image. To do that, dismantle it, which isn't easy, since the kernel image is a combination of boot sector and compressed archive itself. It also depends on the compression is being used for the kernel and for the initramfs. For simplicity, this example assumes bzip2 - however, the principle is the same for other compression methods.

The utility of choice when dismantling kernel images is. It analyzes arbitrary files for known signatures, and prints their offsets. While there are usually a bunch of false matches in the output, it should be easy to pick the correct ones.

A less sophisticated method would be to use to search for signatures. For bzip2, this is BZh. For gzip, use  $'\x1f'$'\x8b' .

In this case the offset we are looking for is 15949 bytes. Now extract the compressed kernel image:

This yeilds the uncompressed kernel image. Somewhere within this image resides the compressed initramfs archive, so just iterate the previous process to find it. Depending on the kernel configuration, it might be another bzip2, gzip, or cpio container.

Suppose the offset is 171424 bytes this time. Now extract the initramfs cpio archive:

To verify that a cpio archive was actually retreived from that, use the command:

Extracting the cpio archive
If the initramfs cpio archive was a separate file, it needs to be uncompressed first.

To extract the uncompressed :

With this, the initramfs structure should have been recovered.

Integrated initramfs does not always update
If the initramfs is integrated into the kernel (instead of using a separate file), there's a possibility that a does not actually update it every time. This could result in making changes to the initramfs but actually keep booting using the old, buggy one. In this case, manually delete the integrated image to force the kernel to integrate a fresh initramfs archive:

Alternatively, run, but then the entire kernel will need to be recompiled.

Command not found
In Gentoo, busybox is configured as standalone shell by default, which allows busybox to execute its own applets directly. Without this setting, Busybox commands (,, ...) won't work unless there is explicitly a symlink created for them. This can be done at the top of the script:

Alternatively, create the symlinks directly in so they will already be included in the initramfs.

Disappearing root
If the encrypted root (with cryptsetup/LUKS), for example, is disappearing after the command it is possible to recreate the device by entering:

Variations for switch_root
Some init setups require proc, sys and dev to be mounted before starting up. If having trouble with in initramfs setup, try replacing the  command with a  in the init script.

For example, replace this.

With this.

Examples
See Custom_Initramfs/Examples for fully functional examples of finished scripts.

External resources

 * Official initramfs documentation locally ( less ) or online at kernel.org
 * Linux® From Scratch - About initramfs