A scalable solution for initial filesystems focused on minimal resource usage, suitable for both critical and non-critical environments.
initoverlayfs is a solution that uses transient overlays as an initial filesystem rather than soley an initramfs. If compression is used, it relies on transparent decompression, rather than upfront decompression. This results in more scalable, maintainable initial filesystems.
Here we see a traditional boot sequence:
fw -> bootloader -> kernel -> initramfs ---------------------------------------> rootfs
fw -> bootloader -> kernel -> init --------------------------------------------------->
Here is the boot sequence with initoverlayfs integrated, the mini-initramfs contains just enough to get storage drivers loaded and storage devices initialized. storage-init is a process that is not designed to replace init, it does just enough to initialize storage, switch-root's to initoverlayfs and continues as normal.
fw -> bootloader -> kernel -> mini-initramfs --------------> initoverlayfs -> rootfs
fw -> bootloader -> kernel -> init ------------------------------------------------>
|
`-initoverlayfs-+
An initramfs (Initial RAM File System) image is a fundamental component in preparing Linux systems during the boot process, preceding the initiation of the init process.
Typically, generating an initramfs involves assembling all available kernel modules and necessary files to boot and support any hardware using the specific Linux kernel version XYZ. This may also include some initialization that's not hardware specific, such as disk encryption, disk verification, early graphics, early camera input, ostree prepare root, etc.
However, this conventional approach presents a significant challenge: loading such a voluminous image into memory during boot is time-consuming and can be problematic in critical scenarios where time to boot is critical. Edge devices commonly need to boot as quickly as possible such as healthcare, automotive and aviation.
Conversely, the initoverlayfs approach proposes a solution: dividing the initramfs image into two parts, relying on transparent decompression rather than upfront decompression.
This division entails segregating the initramfs image into two distinct components.
The first component (initramfs) contains init, kernel modules, udev-rules and an initoverlayfs tool, responsible for setting up and mounting initoverlayfs. Then we switches to the second component (initoverlayfs), containing all additional kernel modules and essential files required to support the Linux boot process.
This scalable approach moves a significant portion on the initial filesystem content to initoverlayfs which is more scalable as it does on-demand decompression.
For illustration, consider a comparison of the sizes using dracut versus initoverlayfs:
Using dracut only:
# dracut -f
# du -sh /boot/initramfs-6.5.5-300.fc39.x86_64.img
36M /boot/initramfs-6.5.5-300.fc39.x86_64.img
Using dracut + initoverlayfs:
# /usr/bin/initoverlayfs-install -f
# du -sh /boot/initramfs-6.5.5-300.fc39.x86_64.img
13M /boot/initramfs-6.5.5-300.fc39.x86_64.img
^^ <--- from 36M to 13M
The advantages of adopting the initoverlayfs approach are evident in the substantial reduction in the size of the initramfs image, thereby significantly expediting the boot process, making it especially appealing in resource-constrained and time-sensitive scenarios.
This is a graphic comparing the boot time effect of increasing initramfs size vs the effect of increasing initoverlayfs size, initoverlayfs is only effected by bytes you use:
This is a graphic comparing the systemd start time using initramfs only vs using initramfs + initoverlayfs on Raspberry Pi 4 with NVMe drive over USB:
- EROFS - Initoverlayfs uses erofs as the underlying filesystem.
- dracut - As the initramfs and initoverlayfs composing tool.
- systemd - As the init system.
Note: none of the above dependencies are strictly needed, all the tools could be swapped out for other similar tools.
Currently, RPM packages are available through the Copr Packages repository.
dnf copr enable -y @centos-automotive-sig/next
dnf install initoverlayfs
Copr repo for next owned by @centos-automotive-sig 2.4 kB/s | 3.3 kB 00:01
Dependencies resolved.
=============================================================================
Package Arch Version Repository Size
=============================================================================
Installing:
initoverlayfs x86_64 0.96-1.fc39 @commandline 22 k
Installing dependencies:
libdeflate x86_64 1.9-7.fc39 fedora 55 k
Installing weak dependencies:
erofs-utils x86_64 1.7.1-1.fc39 updates-testing 140 k
Transaction Summary
=============================================================================
Install 3 Packages
Total size: 217 k
Total download size: 195 k
Installed size: 487 k
Is this ok [y/N]: y
# rpm -qa | grep -i initoverlayfs
initoverlayfs-0.96-1.fc39.x86_64
Note: centos-stream-9 requires package from epel-release
dnf install -y epel-release
Once the deployment is completed, the next step is to execute the /usr/bin/initoverlayfs-install tool. This tool is responsible for generating both the initramfs and initoverlayfs images, along with the essential initoverlayfs.conf configuration.
# /usr/bin/initoverlayfs-install -f
<SNIP>
initoverlayfs
kernel-modules
udev-rules
dracut: Skipping udev rule: 40-redhat.rules
dracut: Skipping udev rule: 50-firmware.rules
dracut: Skipping udev rule: 50-udev.rules
dracut: Skipping udev rule: 91-permissions.rules
dracut: Skipping udev rule: 80-drivers-modprobe.rules
dracut: *** Including modules done ***
dracut: *** Installing kernel module dependencies ***
dracut: *** Installing kernel module dependencies done ***
dracut: *** Resolving executable dependencies ***
dracut: *** Resolving executable dependencies done ***
dracut: *** Hardlinking files ***
dracut: Mode: real
dracut: Method: sha256
dracut: Files: 819
dracut: Linked: 0 files
dracut: Compared: 0 xattrs
dracut: Compared: 77 files
dracut: Saved: 0 B
dracut: Duration: 0.007777 seconds
dracut: *** Hardlinking files done ***
dracut: *** Generating early-microcode cpio image ***
dracut: *** Constructing AuthenticAMD.bin ***
dracut: *** Constructing GenuineIntel.bin ***
dracut: *** Store current command line parameters ***
dracut: *** Stripping files ***
dracut: *** Stripping files done ***
dracut: *** Creating image file '/boot/initramfs-6.5.5-300.fc39.x86_64.img' ***
dracut: Using auto-determined compression method 'pigz'
dracut: *** Creating initramfs image file '/boot/initramfs-6.5.5-300.fc39.x86_64.img' done ***
Excellent! Now, let's proceed to compare the size of the newly generated initramfs image with the existing ones in the filesystem.
# uname -r
6.5.5-300.fc39.x86_64
# du -sh /boot/init*
81M /boot/initramfs-0-rescue-285b2edb8ad94c7381215fd5720afd54.img
34M /boot/initramfs-6.4.12-200.fc38.x86_64.img
34M /boot/initramfs-6.5.5-200.fc38.x86_64.img
13M /boot/initramfs-6.5.5-300.fc39.x86_64.img <- first image to load (storage drivers only)
149M /boot/initoverlayfs-6.5.5-300.fc39.x86_64.img <- second image (extra kernel mods and files)
To load the new generated initramfs and initoverlayfs images a reboot of the system is necessary.
# reboot
To validate whether the new image has been successfully loaded after the reboot, you can execute the following journalctl command and search for the keyword initoverlayfs:
# journalctl -b -o short-monotonic | grep -i initoverlayfs
[ 4.949129] fedora systemd[1]: Queued start job for default target pre-initoverlayfs.target.
[ 5.526459] fedora systemd[1]: Starting pre-initoverlayfs.service - pre-initoverlayfs initialization...
[ 9.179469] fedora initoverlayfs[193]: bootfs: {"UUID=1a3a6db4-a7c2-43e5-bed5-9385f26c68ff", "bootfs UUID=1a3a6db4-a7c2-43e5-bed5-9385f26c68ff"}, bootfstype: {"ext4", "bootfstype ext4"}, fs: {"(null)", "(null)"}, fstype: {"(null)", "(null)"}
[ 9.179469] fedora initoverlayfs[193]: fork_execlp("udevadm")
[ 9.179469] fedora initoverlayfs[193]: forked 199 fork_execlp
[ 9.179469] fedora initoverlayfs[193]: mount("/boot", "/initoverlayfs/boot", "ext4", MS_MOVE, NULL) 2 (No such file or directory)
[ 9.216158] fedora systemd[1]: Finished pre-initoverlayfs.service - pre-initoverlayfs initialization.
[ 9.235546] fedora systemd[1]: Starting pre-initoverlayfs-switch-root.service - Switch Root pre-initoverlayfs...
[ 12.207906] fedora systemd[1]: pre-initoverlayfs-switch-root.service: Deactivated successfully.
[ 12.232609] fedora systemd[1]: Stopped pre-initoverlayfs-switch-root.service.
[ 12.375125] fedora systemd[1]: pre-initoverlayfs.service: Deactivated successfully.
[ 12.393613] fedora systemd[1]: Stopped pre-initoverlayfs.service.
That's fantastic news! The system is up and running smoothly.