Appendix B: Guest Kernels And Root Filesystems

Firecracker starts with two guest artifacts: a kernel image and, unless the kernel contains an initramfs, a root filesystem. They are one boot contract. The kernel must use a format the loader understands, discover the selected virtio transport, include the root device and filesystem drivers, and find a userspace init that can remain PID 1.

This appendix describes that contract. It does not provide a universal kernel config or distribution recipe. Both depend on the Firecracker release, CPU architecture, transport, devices, and userspace being shipped.

Building a kernel does not require /dev/kvm or root privileges. Booting the result does. Keep Firecracker runs on a bare-metal Linux host or in a VM with nested virtualization. The image commands below overwrite the named image file; use a scratch path and check it before running them.

Start With The Support Policy

Firecracker continuously validates combinations drawn from its supported host and guest kernel tables. As of Firecracker source commit fb53569b807c8e1349e4d2b49d5300e463a91d73, the guest table is:

Page Size Guest Kernel First Firecracker Release Minimum Support Date
4K 5.10 1.0.0 2024-01-31
4K 6.1 1.9.0 2026-09-02

The last column is a minimum end-of-support date, not an automatic removal date. A version remains supported while it remains in the current table. Check the policy for the Firecracker release being deployed rather than inferring support from the calendar.

The repository also contains 6.18 guest config files for both architectures, but the guest support table does not list 6.18. Their presence is not, by itself, a support declaration.

Choose The Transport First

Firecracker creates virtio devices on MMIO by default. Starting Firecracker with --enable-pci instead creates all virtio devices on the PCI transport. This is a machine-wide choice, not a per-device setting.

Transport Kernel Configuration Boot Command Line
MMIO CONFIG_VIRTIO_MMIO=y and each required device driver Firecracker appends pci=off when PCI is disabled
PCI CONFIG_PCI=y, CONFIG_PCI_MMCONFIG=y, CONFIG_PCI_MSI=y, CONFIG_PCIEPORTBUS=y, CONFIG_BLK_MQ_PCI=y, CONFIG_VIRTIO_PCI=y Must not contain pci=off

On aarch64, PCI also needs CONFIG_PCI_HOST_COMMON=y and CONFIG_PCI_HOST_GENERIC=y. On x86_64, CONFIG_PCI=y is also required for the ACPI boot path even when the devices themselves use MMIO.

Do not confuse transport with device type. CONFIG_VIRTIO_MMIO or CONFIG_VIRTIO_PCI lets Linux reach a virtio device; CONFIG_VIRTIO_BLK, CONFIG_VIRTIO_NET, and the other device-specific options let Linux drive it. Anything needed before the root filesystem is mounted must be built in with =y, unless an initramfs supplies the module and loads it first.

Build The Boot Path

Firecracker's policy gives the following minimal starting points. A useful kernel normally adds logging, the selected transport, the root filesystem driver, and any devices the workload actually uses.

Boot Source Architecture Policy Minimum
Initrd x86_64 CONFIG_BLK_DEV_INITRD=y, CONFIG_KVM_GUEST=y
Initrd aarch64 CONFIG_BLK_DEV_INITRD=y, CONFIG_VIRTIO_MMIO=y
Root block device x86_64 CONFIG_VIRTIO_BLK=y, CONFIG_ACPI=y, CONFIG_PCI=y, CONFIG_KVM_GUEST=y
Root block device aarch64 CONFIG_VIRTIO_BLK=y

For an ext4 root image, add CONFIG_EXT4_FS=y. A kernel that must locate a partition by PARTUUID also needs the relevant partition parser; Firecracker's policy names CONFIG_MSDOS_PARTITION. For useful serial diagnostics, enable CONFIG_SERIAL_8250_CONSOLE=y and CONFIG_PRINTK=y.

The rest follows the configured devices:

Function Built-In Options
Network CONFIG_VIRTIO_NET=y
Vsock CONFIG_VIRTIO_VSOCKETS=y
Entropy device CONFIG_HW_RANDOM_VIRTIO=y
Balloon CONFIG_MEMORY_BALLOON=y, CONFIG_VIRTIO_BALLOON=y
Hotplugged memory CONFIG_VIRTIO_MEM=y
Persistent memory CONFIG_VIRTIO_PMEM=y

These are conditional requirements, not a checklist for every guest. For example, a guest with no vsock device does not need the vsock driver. The same rule applies to filesystems, security modules, and network protocols: derive the config from the actual guest contract.

On x86_64, CONFIG_KVM_GUEST=y enables KVM-aware guest support, including the KVM clock dependency in the supported configurations. Firecracker's clean shutdown path also uses CONFIG_SERIO_I8042=y and CONFIG_KEYBOARD_ATKBD=y. On aarch64, the policy calls out CONFIG_ARM_AMBA=y, CONFIG_RTC_DRV_PL031=y, and CONFIG_SERIAL_OF_PLATFORM=y for time and serial support.

Treat The CI Configs As Coupled Artifacts

resources/guest_configs/ contains base configs for aarch64 and x86_64 at 5.10, 6.1, and 6.18, plus an x86_64 5.10 no-ACPI variant. The directory also contains ci.config, debug.config, and ftrace.config overlays.

Those files are evidence of what Firecracker CI builds; they are not generic upstream Linux configs. The kernel policy says they are used with Amazon Linux microvm-kernel-* trees, which can contain required backports, and explicitly does not guarantee that the configs produce usable images from the equivalent upstream kernel.

For an exact CI artifact, use Firecracker's documented build entry point from a checked-out release:

./tools/devtool build_ci_artifacts kernels 6.1

This is a resource-intensive repository build, not a host configuration step. For a custom upstream kernel, begin with the target tree's architecture config, select options from the boot contract above, run make olddefconfig, and inspect the resulting .config. Copying a same-numbered Firecracker config across kernel trees skips the compatibility question that matters most.

The CI configs are deliberately narrow. They disable modules and many hardware subsystems, and their DISCLAIMER.md describes them as optimized for high-density, ephemeral workloads. Review their security, filesystem, namespace, debugging, and observability choices against the workload instead of assuming that smaller means production-ready.

Produce The Loader Artifact

The output differs by architecture:

Architecture Build Target Firecracker Artifact
x86_64 make vmlinux Uncompressed ELF vmlinux
aarch64 make Image PE-formatted arch/arm64/boot/Image

On x86_64, Firecracker's ELF loader uses the PVH entry when the image contains the corresponding note; CONFIG_PVH=y is present in the 6.1 CI config. The loader otherwise follows its Linux boot-protocol path. Do not substitute arch/x86/boot/bzImage for the documented uncompressed ELF artifact.

On aarch64, Firecracker uses its PE loader for Image. The 6.1 CI config contains CONFIG_EFI=y and CONFIG_EFI_STUB=y, which produce the expected image form; this does not turn the microVM into a general EFI firmware environment.

Understand Discovery And Boot Arguments

With x86_64 ACPI boot, Firecracker describes the machine in ACPI tables. The legacy no-ACPI path instead describes virtio-MMIO devices with virtio_mmio.device= command-line entries and requires CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y. Firecracker's policy deprecates that legacy mechanism. On aarch64, the device tree supplies the MMIO layout.

If the API request omits boot_args, current Firecracker starts from:

reboot=k panic=1 nomodule 8250.nr_uarts=0 i8042.noaux i8042.nomux i8042.dumbkbd swiotlb=noforce

A supplied boot_args value replaces that default. Firecracker still augments the resulting line for its configured machine: it adds pci=off when PCI is disabled and derives root=/dev/vda or root=PARTUUID=..., plus ro or rw, from the root block drive. A root pmem device similarly becomes root=/dev/pmemN. Do not add a second root= argument when the API already marks a drive as the root device.

Add a console argument appropriate to the guest if logs must be visible. The common x86_64 choice is console=ttyS0; aarch64 early-console requirements differ by kernel config. A silent guest is not evidence that the kernel did not run.

Define The Root Filesystem Contract

A root image needs more than a filesystem signature. Before building it, answer four questions:

  1. Which block transport and filesystem drivers are built into the kernel?
  2. Which executable will become PID 1?
  3. Is that executable static, or are its interpreter and shared libraries present?
  4. Which paths must remain writable after the root is mounted?

Linux first tries /init for an initramfs. For the final root it honors an init= override, then any configured default, then tries /sbin/init, /etc/init, /bin/init, and /bin/sh. Failure to execute any candidate ends in a kernel panic. A successful PID 1 must perform the initialization the guest needs and must not simply exit.

At minimum, a conventional root tree supplies the chosen init and its runtime dependencies, plus mount points such as /dev, /proc, /sys, /run, and /tmp. CONFIG_DEVTMPFS=y lets the kernel provide device nodes; CONFIG_DEVTMPFS_MOUNT=y can mount devtmpfs automatically when the kernel mounts the final root directly. An initramfs or custom init may need to mount it explicitly.

Preserve numeric ownership, modes, symlinks, extended attributes, and file capabilities while assembling the tree. Those are part of the guest's security boundary. A read-only root also needs writable tmpfs mounts or data volumes for state such as /run, temporary files, logs, and application data.

Build Ext4 Without Mounting It

mke2fs -d copies an existing directory into a newly created filesystem. It avoids loop mounts and root privileges, which makes the artifact boundary easier to audit:

truncate -s 256M rootfs.ext4
mke2fs -t ext4 -d rootfs-tree rootfs.ext4
e2fsck -fn rootfs.ext4

The first two commands create or overwrite rootfs.ext4; verify that name and run them only in a scratch artifact directory. rootfs-tree must be a prepared directory, not a tar archive. The final command performs a read-only check.

This method does not solve provenance. Construct rootfs-tree from pinned packages or reproducible build outputs, keep a manifest, and scan the finished artifact. If a distribution-specific tool requires containers, mounts, loop devices, or root, run that workflow in an isolated build VM and treat the result as untrusted until validated.

Validate The Pair

Before booting, inspect the artifacts independently:

Only the final boot test needs KVM. Run it in the isolated virtualization host, capture the serial log, and keep the tested kernel, rootfs, Firecracker binary, and configuration as one versioned set.

Sources