Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Initramfs

Overview

initramfs (initial RAM filesystem) is a temporary root filesystem loaded into memory by the bootloader. It runs the first userspace process (init) before the real root filesystem is mounted. Initramfs handles tasks that must happen before the root filesystem is available:

  • Loading kernel modules needed to access the root device
  • Assembling RAID arrays or LVM volumes
  • Unlocking encrypted root partitions (LUKS)
  • Running early userspace utilities
  • Pivoting to the real root filesystem

See also: Boot Process, dracut, Root Filesystem


cpio Format

What is cpio?

Initramfs uses the cpio (copy in/out) archive format. Unlike an initrd (which is a filesystem image), initramfs is a cpio archive that the kernel unpacks into a tmpfs:

Bootloader → Kernel → Unpacks cpio into tmpfs → Runs /init

Creating a Basic cpio Archive

# Create a minimal initramfs
mkdir -p initramfs/{bin,sbin,etc,proc,sys,dev}

# Copy busybox (statically linked)
cp /bin/busybox initramfs/bin/
cd initramfs/bin
for cmd in sh mount umount mkdir cat echo ls; do
    ln -s busybox $cmd
done
cd ../..

# Create the init script (see below)
# ...

# Pack as cpio
cd initramfs
find . | cpio -o -H newc | gzip > ../initramfs.cpio.gz
cd ..

cpio Formats

FormatDescriptionKernel Support
newcSVR4 “new” format (required)Yes
odcPOSIX.1 portable formatYes
crcLike newc but with CRCYes

The kernel requires newc or crc format for initramfs.

Multiple cpio Archives

The kernel can unpack multiple concatenated cpio archives:

# Combine base + microcode + custom
cat base.cpio.gz intel-ucode.cpio.gz custom.cpio.gz > initramfs.cpio.gz

This is how CPU microcode updates are applied early in boot.


The init Script

Minimal init Script

#!/bin/sh
# /init — First userspace process in initramfs

# Mount essential filesystems
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev

# Load necessary modules
modprobe ext4
modprobe ahci

# Wait for root device to appear
echo "Waiting for root device..."
while [ ! -b /dev/sda1 ]; do
    sleep 0.1
done

# Mount the real root
mount -t ext4 /dev/sda1 /mnt/root

# Clean up
umount /proc
umount /sys
umount /dev

# Pivot to real root
exec switch_root /mnt/root /sbin/init

More Complete Example

#!/bin/sh
set -e

PATH=/sbin:/bin:/usr/sbin:/usr/bin

# Mount virtual filesystems
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mount -t tmpfs tmpfs /tmp

# Parse kernel command line
ROOT=""
for param in $(cat /proc/cmdline); do
    case $param in
        root=*) ROOT="${param#root=}";;
        rootdelay=*) ROOTDELAY="${param#rootdelay=}";;
    esac
done

# Wait for root device
if [ -n "$ROOTDELAY" ]; then
    sleep "$ROOTDELAY"
fi

# Handle different root device types
case "$ROOT" in
    UUID=*)
        ROOT_DEV=$(blkid -U "${ROOT#UUID=}")
        ;;
    LABEL=*)
        ROOT_DEV=$(blkid -L "${ROOT#LABEL=}")
        ;;
    /dev/*)
        ROOT_DEV="$ROOT"
        ;;
    *)
        echo "Unknown root device: $ROOT"
        exec /bin/sh
        ;;
esac

# Load filesystem module
FSTYPE=$(blkid -s TYPE -o value "$ROOT_DEV")
modprobe "$FSTYPE" 2>/dev/null || true

# Mount root read-only first
mount -t "$FSTYPE" -o ro "$ROOT_DEV" /mnt/root

# Optionally mount submounts
if [ -f /mnt/root/etc/fstab ]; then
    # Mount /proc, /sys, /dev in new root
    mount -t proc proc /mnt/root/proc
    mount -t sysfs sysfs /mnt/root/sys
    mount -o bind /dev /mnt/root/dev
fi

# Switch to real root
echo "Switching to real root..."
exec switch_root /mnt/root /sbin/init

# If switch_root fails, drop to shell
echo "Failed to switch root!"
exec /bin/sh

Kernel Command Line

The kernel passes parameters to init via /proc/cmdline:

root=/dev/sda1 ro rootfstype=ext4 rootdelay=5 init=/sbin/init
ParameterDescription
root=Root device (UUID, LABEL, or path)
rootfstypeRoot filesystem type
rootdelaySeconds to wait for root device
init=Path to init program on root
rd.*dracut-specific parameters

gen_init_cpio

Purpose

gen_init_cpio is a kernel utility that creates cpio archives from a specification file. It provides more control than find | cpio.

Building gen_init_cpio

# It's in the kernel source
cd /usr/src/linux
make usr/gen_init_cpio

Specification File Format

# /path/to/initramfs.list

# Directory
dir /bin 755 0 0
dir /sbin 755 0 0
dir /etc 755 0 0
dir /proc 755 0 0
dir /sys 755 0 0
dir /dev 755 0 0

# File with inline content
file /init /path/to/init.sh 755 0 0

# File from host filesystem
file /bin/busybox /bin/busybox 755 0 0

# Symlink
slink /bin/sh /bin/busybox 755 0 0

# Special files
nod /dev/console 644 0 0 c 5 1
nod /dev/null 644 0 0 c 1 3
nod /dev/tty 644 0 0 c 5 0
nod /dev/sda1 644 0 0 b 8 1

# Concatenate external cpio
# external_cpio /path/to/extra.cpio

Generating the Archive

# From specification file
./usr/gen_init_cpio initramfs.list | gzip > initramfs.cpio.gz

# Compressed
./usr/gen_init_cpio initramfs.list | xz > initramfs.cpio.xz

Embedded in Kernel

# Embed initramfs in kernel image
# In .config:
CONFIG_INITRAMFS_SOURCE="/path/to/initramfs.list"
# Then build the kernel — initramfs is built into vmlinuz

dracut

What is dracut?

dracut is an event-driven initramfs infrastructure used by Fedora, RHEL, CentOS, SUSE, and others. It generates initramfs images automatically based on the current system configuration.

Basic Usage

# Generate initramfs for current kernel
dracut /boot/initramfs-$(uname -r).img $(uname -r)

# Force regeneration
dracut --force /boot/initramfs-$(uname -r).img $(uname -r)

# Add specific modules
dracut --add "lvm crypt" /boot/initramfs-$(uname -r).img $(uname -r)

# List included modules
dracut --list-modules

# Show what would be included (dry run)
dracut --print-cmdline

dracut Modules

dracut is modular — each module adds specific functionality:

ModuleDescription
baseCore initramfs functionality
lvmLVM volume assembly
cryptLUKS/dm-crypt decryption
mdraidSoftware RAID assembly
nfsNFS root filesystem
iscsiiSCSI root device
multipathMultipath device handling
plymouthBoot splash screen
systemdsystemd as init in initramfs
dracut-systemdsystemd-based dracut boot

dracut Configuration

# /etc/dracut.conf.d/custom.conf
# Add LVM and crypto support
add_dracutmodules+=" lvm crypt "

# Include specific kernel modules
add_drivers+=" ahci ext4 "

# Set compression
compress="xz"

# Host-only mode (default: yes)
hostonly="yes"

# Add custom files
install_items+=" /usr/bin/cryptsetup "

dracut Command Line

# Add modules
dracut --add "lvm crypt" initramfs.img

# Exclude modules
dracut --omit "network plymouth" initramfs.img

# Include firmware
dracut --fwdir /lib/firmware initramfs.img

# Minimal initramfs
dracut --hostonly --no-hostonly-cmdline initramfs.img

# Debug mode
dracut --debug initramfs.img

Other initramfs Generators

mkinitcpio (Arch Linux)

# Generate initramfs
mkinitcpio -P  # All presets
mkinitcpio -p linux  # Specific preset

# Configuration: /etc/mkinitcpio.conf
HOOKS="base udev autodetect modconf block filesystems keyboard fsck"

initramfs-tools (Debian/Ubuntu)

# Generate initramfs
update-initramfs -u  # Update current kernel
update-initramfs -c -k $(uname -r)  # Create new

# Configuration: /etc/initramfs-tools/

Comparison

ToolDistributionConfig Location
dracutFedora, RHEL, SUSE/etc/dracut.conf.d/
mkinitcpioArch Linux, Manjaro/etc/mkinitcpio.conf
initramfs-toolsDebian, Ubuntu/etc/initramfs-tools/

Root Pivot (switch_root)

The Pivot Process

switch_root is the critical step that transitions from initramfs to the real root:

# 1. Mount real root
mount /dev/sda1 /mnt/root

# 2. Move virtual filesystems
mount --move /proc /mnt/root/proc
mount --move /sys /mnt/root/sys
mount --move /dev /mnt/root/dev

# 3. Switch root
exec switch_root /mnt/root /sbin/init

How switch_root Works

  1. Deletes all files in the initramfs (frees memory)
  2. chdir to the new root
  3. chroot to the new root
  4. exec the init program

Alternative: pivot_root

pivot_root is a lower-level syscall that switch_root wraps:

#include <sys/syscall.h>
#include <unistd.h>

/* pivot_root(new_root, put_old) */
syscall(__NR_pivot_root, "/mnt/root", "/mnt/root/initramfs");
# Manual pivot_root
cd /mnt/root
mkdir -p .initramfs
pivot_root . .initramfs
exec chroot . /sbin/init

Debugging Initramfs

Extracting an Existing Initramfs

# Check the format
file /boot/initramfs-$(uname -r).img

# Extract (gzip-compressed cpio)
mkdir /tmp/initramfs
cd /tmp/initramfs
zcat /boot/initramfs-$(uname -r).img | cpio -idmv

# For xz-compressed
xzcat /boot/initramfs-$(uname -r).img | cpio -idmv

# For newer systems with unified kernel images
objcopy -O binary -j .initrd /boot/efi/EFI/Linux/linux.efi initramfs.cpio

Adding Debug Shell

# In init script, add:
exec /bin/sh

# Or drop to shell on error
mount /dev/sda1 /mnt/root || {
    echo "Failed to mount root!"
    exec /bin/sh
}

Breakpoints in dracut

# Boot with dracut breakpoints
# Add to kernel command line:
# rd.break=pre-mount rd.break=mount

# Available breakpoints:
# pre-udev, pre-trigger, pre-mount, mount, pre-pivot, pre-shutdown

Verbose Boot

# Add to kernel command line:
# rd.shell rd.debug rd.log=console

# This shows:
# - All dracut actions
# - Shell on errors
# - Full debug logging

Advanced Topics

Custom Init Programs

initramfs doesn’t have to run shell scripts. Any statically-linked binary can be /init:

/* minimal_init.c */
#include <sys/mount.h>
#include <unistd.h>
#include <stdio.h>

int main(void)
{
    mount("proc", "/proc", "proc", 0, NULL);
    mount("sysfs", "/sys", "sysfs", 0, NULL);
    mount("devtmpfs", "/dev", "devtmpfs", 0, NULL);

    /* Load modules, unlock crypto, etc. */

    mount("/dev/sda1", "/mnt/root", "ext4", MS_RDONLY, NULL);

    chdir("/mnt/root");
    mount(".", "/", NULL, MS_MOVE, NULL);
    chroot(".");
    chdir("/");

    execl("/sbin/init", "init", NULL);
    return 1;
}

Microcode Updates

CPU microcode must be loaded before any user code runs. The initramfs mechanism handles this:

# Intel microcode
cpio -idmv < /boot/intel-ucode.img

# AMD microcode
cpio -idmv < /boot/amd-ucode.img

# Concatenated with main initramfs
cat /boot/intel-ucode.img /boot/initramfs-$(uname -r).img > /tmp/combined.img

Encrypted Root (LUKS)

#!/bin/sh
# init script for encrypted root

mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev

modprobe dm-crypt
modprobe aes

# Prompt for passphrase
/lib/cryptsetup/askpass "Enter passphrase: " | \
    cryptsetup luksOpen /dev/sda1 cryptroot

mount /dev/mapper/cryptroot /mnt/root

exec switch_root /mnt/root /sbin/init

Size Optimization

# Use busybox instead of individual binaries
# Use static linking
# Strip binaries
strip --strip-all initramfs/bin/*

# Use xz compression (best ratio)
find . | cpio -o -H newc | xz -9 --check=crc32 > initramfs.cpio.xz

# Remove unnecessary files
rm -rf initramfs/usr/share/locale
rm -rf initramfs/usr/share/man

Further Reading

Related topics: Boot Process, Kernel Command Line, Root Filesystem, LUKS/dm-crypt