Initramfs

cache22 splits the initramfs into separate img files. The UKI builder (/usr/libexec/cache22/resign-uki) assembles them into one UKI at install and on every bootc upgrade. The kernel unpacks concatenated cpio segments, so several imgs combine into one initramfs at boot.

The imgs

Base img

/usr/lib/modules/<kver>/initramfs.img, built by scripts/generate-initramfs.sh. It is the bare minimum needed to find the root filesystem and unlock LUKS: storage controllers, btrfs/xfs/ext4, device-mapper, dm-crypt, TPM2, the boot stack (bootc, ostree, systemd, and the erofs + overlay modules ostree-prepare-root needs to mount the composefs root), and basic input for typing a passphrase.

The base img is always sufficient to boot on its own. Tooling that loads only it (an older resign-uki, or an ostree BLS entry before the builder has rewritten it) still produces a bootable system.

Microcode img

/usr/lib/cache22/microcode.img, also built by generate-initramfs.sh. CPU microcode is independent of the kernel version, so it is built once and shipped as its own img. Both Intel and AMD microcode are included; the CPU applies whichever vendor matches. resign-uki loads it first, ahead of the main initramfs, because the CPU applies microcode before the main initramfs is unpacked.

Keeping microcode out of the base img (early_microcode=no) means a microcode package bump no longer re-pulls the whole initramfs layer.

User img (optional)

/var/lib/cache22/initramfs/user-<kver>.img, built per machine by /usr/libexec/cache22/build-user-initrd when you configure additions. When present, resign-uki uses it in place of the base img. See Customizing below.

Extra segments (extensibility)

resign-uki also globs /usr/lib/cache22/initramfs.d/*.img in sorted order and folds any imgs found there into the UKI, after the base img. The directory is empty today. It exists so a future image can split high-churn content out of the base img (for example firmware, or a driver set) into its own img, and therefore its own OCI layer, by simply shipping a numbered img there. Because resign-uki globs this directory rather than hardcoding names, the resign-uki already deployed on a machine picks up such a new img on the next upgrade with no code change. The microcode and base imgs stay hardcoded: microcode must load first, and the base img is the kernel-specific anchor ostree manages.

Assembly

/usr/libexec/cache22/cache22-initramfs-list enumerates the ordered img set for a deploy (microcode, then the user override or base img, then sorted extra segments) and builds the user img on demand. It is the single source of truth for what the initramfs contains; both firmware paths consume it, so they cannot disagree:

  • UEFI: resign-uki passes each img to ukify as a --initrd and writes one signed UKI per deploy to the ESP. sd-stub loads the concatenated segments.
  • BIOS: cache22-bios-initramfs concatenates the same imgs into one initrd file per BLS entry on the /boot partition (/cache22/<entry>/initrd.img) and rewrites the entry’s initrd= line to point at it. GRUB loads the single file and the kernel unpacks the concatenated cpio segments. A single file is the format ostree already writes and GRUB unambiguously supports, so the BIOS path carries no dependency on blscfg multi-initrd handling.

Both run at install, at finalize (chrooted into the new deploy via resign-uki-finalize, so the build uses the new deploy’s modules and config), and at runtime (cache22-resign-uki.service, dispatched by firmware through cache22-boot-rebuild).

What is excluded, and why

Out-of-tree and DKMS modules (nvidia, zfs, the Realtek and Intel NIC DKMS drivers, and so on) are excluded from every img. They are not needed to reach the root filesystem: the GPU and network come up from the real root via udev after switch_root, and cache22 never uses a ZFS root. They also change on every driver bump, which would re-pull the initramfs layer. The exclusion is written dynamically by generate-initramfs.sh into /usr/lib/dracut/dracut.conf.d/20-cache22-omit-dkms.conf by enumerating updates/ and extramodules/, so new DKMS modules are excluded automatically.

The dracut config (/usr/lib/dracut/dracut.conf.d/10-cache22.conf) also omits modules that are not on the boot path for a local btrfs/xfs/ext4 root over optional LUKS: network-root (iscsi/nfs/cifs), lvm, mdraid, nvdimm, resume, fido2, pkcs11, hwdb, the VM share/net helpers, and module-signature checking (enforcement is off). Some of these (lvm, mdraid, resume) may return as their own imgs if cache22 grows support for them.

Customizing

To add drivers or other content to your machine’s initramfs, for example a NIC driver and an SSH client so the initramfs can fetch a remote LUKS key:

  1. Drop a dracut config file into /etc/dracut.conf.d/. It is read on top of the image default. Example /etc/dracut.conf.d/90-remote-unlock.conf:

    add_drivers+=" i40e "
    add_dracutmodules+=" network-manager "
    install_items+=" /usr/bin/curl "
    
  2. Apply it. This rebuilds the running deploy’s user img from the current config and folds it into the boot-time initramfs (the UKI on UEFI, the combined initrd on BIOS):

    sudo systemctl start cache22-resign-uki.service
    

    It is also applied automatically on the next bootc upgrade or reboot. To build the img by hand, for example for a kernel version that is not running, run sudo /usr/libexec/cache22/build-user-initrd [kver].

resign-uki builds the user img for each live deploy in that deploy’s own context. For the deploy it can build in context (the running deploy, or the deploy being staged at finalize) it rebuilds the img on every run, so editing /etc/dracut.conf.d and re-running it picks up the change instead of reusing a stale img. For any other deploy (a rollback) it builds only when that deploy has no img yet, by chrooting into it; that deploy’s /etc is frozen, so it needs no rebuild otherwise. A deploy whose /etc carries no override keeps the base img. The img is built to a temp file and renamed, so a failed rebuild never replaces a working img.

Because the override lives in /etc, which ostree merges forward onto each new deploy, a customization applies to the current deploy and to every deploy created afterward. A deploy that predates the override (an existing rollback) stays on the base img and still boots. On a kernel update no manual step is needed: the new deploy’s initramfs is built at finalize against that deploy’s own kernel modules. If a build fails, that deploy falls back to the base img with a warning, so the machine still boots.

Firmware parity

The microcode, user, and extra imgs apply on both UEFI and BIOS: UEFI carries them as UKI sections, BIOS as the per-entry combined initrd (see Assembly). The one thing BIOS does not get is the UEFI-only security chain (Secure Boot signing and the TPM2 PCR measurements sd-stub makes); the initramfs contents are identical. Microcode is included on BIOS for completeness; a virtual-machine guest ignores it (the host applies CPU microcode), but a bare-metal BIOS install applies it.