Appendix B: Building Guest Kernels And Root Filesystems
Firecracker will reject a kernel that does not speak its exact dialect of virtio-MMIO, and it will fail to boot a rootfs whose init it cannot find. Both problems are silent in the same way: the VM exits without a useful message, or the serial console emits Kernel panic - not syncing: No working init found. and stops. This appendix is the reference for avoiding both. It covers which kernel versions Firecracker supports and why, the exact Kconfig options that must be set and the reasoning behind each, how the CI config files in resources/guest_configs/ are constructed and what their deliberate omissions cost you, how to compile the kernel to get the right output artifact (vmlinux on x86_64, arch/arm64/boot/Image on aarch64), and how to construct a minimal ext4 root filesystem with a working init.
Supported Guest Kernel Versions
Firecracker's kernel policy is conservative by design: the project guarantees at least two supported major guest kernel versions at any point in time, and a version receives at minimum two years of support from the date it is officially added. The current floor is Linux 6.1.
The history is short enough to state precisely:
- Linux 4.14 was supported from Firecracker v1.0.0 and removed in v1.10.0.
- Linux 5.10 was supported from v1.0.0. Its end-of-support date was 2024-01-31, and it was dropped in v1.10.0.
- Linux 6.1 was added in v1.9.0 with an end-of-support date of 2026-09-02.
All tested configurations use a 4K page size. The policy explicitly rejects huge-page-only kernel configs: nothing in the project's CI exercises a 64K-page guest.
What The Kernel Must Have, And Why
The guest kernel is not a general-purpose Linux build. Firecracker exposes a fixed device model — virtio-MMIO transport, 8250 UART, i8042 keyboard controller, and (since v1.8.0) ACPI tables — and the kernel must have drivers for exactly that model. Everything else is optional overhead.
The minimum requirement that cuts across both architectures is the virtio-MMIO transport. Firecracker emulates no PCI bus by default. Every virtio device — block, network, console, vsock, balloon, entropy — is attached over the MMIO transport described in virtio 1.2 spec §4.2. The driver for that transport is CONFIG_VIRTIO_MMIO=y. The option carries two dependencies that must be satisfied: HAS_IOMEM and HAS_DMA, both present on any sane architecture. Setting it also selects CONFIG_VIRTIO, which enables the virtio core; you do not need to set CONFIG_VIRTIO separately.
With the transport in place, the device drivers follow. CONFIG_VIRTIO_BLK=y is the block device driver that exposes the rootfs; it depends on VIRTIO and selects SG_POOL for scatter-gather. CONFIG_VIRTIO_NET=y is the network driver; it selects NET_FAILOVER. Both must be built in — =y, not =m — because CONFIG_MODULES=n is enforced in the Firecracker CI configs. The kernel can load no modules at all; anything needed at boot must be compiled into the image.
The console requires two options. CONFIG_SERIAL_8250=y enables the 8250 UART family driver, and CONFIG_SERIAL_8250_CONSOLE=y makes that UART usable as the kernel console target. Firecracker emulates a single 8250 UART accessible as ttyS0. Without these options the kernel boots into silence: no boot messages, no shell prompt, no panic text. CONFIG_PRINTK=y is the prerequisite for any kernel log output and must be explicitly confirmed on because some stripped configs set CONFIG_PRINTK=n to reduce binary size.
Additional options that must be present across both architectures:
CONFIG_BLK_DEV_INITRD=y enables initrd and initramfs support. Even when you are booting directly from an ext4 rootfs without an initramfs, this option gates the rdinit= boot parameter and is required by some init systems.
CONFIG_VIRTIO_VSOCKETS=y and CONFIG_HW_RANDOM_VIRTIO=y enable vsock guest–host communication and the virtio entropy device respectively. Both are common enough in production Firecracker deployments that omitting them will break things that look unrelated — the vsock device is how many Firecracker host-to-guest agents communicate; the entropy device seeds the in-kernel CSPRNG at boot.
CONFIG_MEMORY_BALLOON=y and CONFIG_VIRTIO_BALLOON=y enable memory ballooning, which Firecracker added in v1.4.0. CONFIG_RANDOM_TRUST_CPU=y tells the kernel to accept the CPU's hardware RNG (RDRAND on x86, equivalent on aarch64) as a trusted entropy source at boot, which shortens the entropy-starvation window that otherwise delays getrandom callers.
The x86_64 Options
On x86_64 three categories of options matter beyond the universal set: KVM paravirtualization, ACPI, and a legacy keyboard emulation.
CONFIG_KVM_GUEST=y enables the paravirtual operations the KVM guest side implements — the important one is pv_time_ops, which corrects timekeeping. Without it, timekeeping accuracy degrades: the guest loses access to the KVM paravirtual clock source. CONFIG_KVM_CLOCK=y enables the KVM clock source itself, which reads from a shared memory page the host updates with real nanosecond timestamps.
ACPI deserves explanation because the requirement changed across Firecracker versions. Before v1.8.0, Firecracker used the legacy MPTable to describe CPUs and devices to the guest. Since v1.8.0, it generates ACPI tables: an FADT in Hardware-Reduced mode, an MADT (one IOAPIC plus one LocalAPIC per vCPU, replacing MPTable), and a DSDT containing AML bytecode that describes each virtio device and each legacy x86 device. The ACPI PR was merged on 2024-03-25 (PR #4428 in the Firecracker repository). CONFIG_ACPI=y in the guest enables the code path that reads those tables; without it the guest cannot enumerate its devices on current Firecracker versions. CONFIG_PCI=y is required even though Firecracker does not expose a PCI bus: the ACPI initialization code walks PCI configuration space during startup, and the kernel will fail to boot with ACPI enabled unless the PCI subsystem is compiled in.
One important qualifier on what CONFIG_ACPI=y does not do: Firecracker's FAQ states directly that it does not virtualize power management. The FADT is in Hardware-Reduced mode, which disables the legacy ACPI power management registers. CONFIG_ACPI_SLEEP=y appears in the CI config but S-state entry is non-functional; the option exists to avoid breaking code that assumes the symbol is defined, not to enable sleep states.
CONFIG_SERIO_I8042=y and CONFIG_KEYBOARD_ATKBD=y are required for a different reason: Firecracker implements the i8042 keyboard controller as the only reboot path. The kernel command line parameter reboot=k instructs Linux to perform an i8042 reset on reboot. Without the i8042 driver, the reset does not happen and the VM hangs.
CONFIG_PTP_1588_CLOCK=y and CONFIG_PTP_1588_CLOCK_KVM=y enable the KVM PTP/PHC driver, which provides a hardware clock interface for guests that need precise timestamps.
CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y is a special case. It exists only for the legacy no-ACPI discovery path: when Firecracker is run without ACPI (or when using a kernel older than when Firecracker required ACPI), Firecracker appends virtio_mmio.device=<size>@<base>:<irq> stanzas to the kernel command line for each device. The Kconfig help text for this option includes a pointed warning: "using incorrect parameters (base address in particular) can crash your system." Do not set it when building with ACPI enabled — the 6.1 CI config omits it entirely — because it has no function on that path and the risk of misconfiguration is real.
The aarch64 Options
aarch64 requires a different set of architecture-specific options reflecting the ARM device model:
CONFIG_ARM_AMBA=y enables the ARM AMBA bus, and CONFIG_RTC_DRV_PL031=y enables the PL031 real-time clock driver. CONFIG_SERIAL_OF_PLATFORM=y allows the kernel to discover the serial console from the device tree rather than a fixed address, and CONFIG_HVC_DRIVER=y enables the hypervisor virtual console. These replace the x86 equivalents (8250, i8042, ACPI) in the aarch64 device model.
CONFIG_EFI=y and CONFIG_EFI_STUB=y are required because the aarch64 kernel is a PE-format image that Firecracker boots as an EFI stub. The build output artifact is arch/arm64/boot/Image, not vmlinux. This is not an EFI runtime environment — the stub protocol is used only for the boot handshake — but the kernel must be compiled with EFI support to produce that image format.
The interrupt controller options replace x86's APIC stack. CONFIG_ARM_GIC=y enables the ARM Generic Interrupt Controller, CONFIG_ARM_GIC_V3=y enables the GICv3 variant, and CONFIG_ARM_GIC_V3_ITS=y enables the ITS (Interrupt Translation Service) for MSI support. CONFIG_ARM_ARCH_TIMER=y enables the ARM architectural timer, which replaces the x86 PIT and HPET.
ACPI on aarch64 uses the Hardware-Reduced profile from the start: CONFIG_ACPI_REDUCED_HARDWARE_ONLY=y. Full ACPI table support requires CONFIG_ACPI_IORT=y (I/O Remapping Table), CONFIG_ACPI_GTDT=y (Generic Timer Description Table), CONFIG_ACPI_PPTT=y (Processor Properties Topology Table), and CONFIG_ACPI_SPCR_TABLE=y (Serial Port Console Redirection Table).
The CI Kernel Config Files
The resources/guest_configs/ directory in the Firecracker repository contains the reference configs used in every CI run. They are the most important configs to understand: every Firecracker binary is tested against guest kernels built from these files, and the project considers a kernel that fails to boot one of them a Firecracker bug. There are six config files and three overlay fragments:
| Filename | Arch | Kernel | Notes |
|---|---|---|---|
microvm-kernel-ci-x86_64-5.10.config |
x86_64 | 5.10 | ACPI enabled |
microvm-kernel-ci-x86_64-5.10-no-acpi.config |
x86_64 | 5.10 | MPTable path; CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y |
microvm-kernel-ci-x86_64-6.1.config |
x86_64 | 6.1 | ACPI enabled; recommended starting point |
microvm-kernel-ci-aarch64-5.10.config |
aarch64 | 5.10 | ACPI enabled |
microvm-kernel-ci-aarch64-6.1.config |
aarch64 | 6.1 | ACPI enabled; 48-bit VA space |
DISCLAIMER.md |
-- | -- | Rationale for stripped-down defaults |
ci.config |
overlay | all | Additional CI-specific options applied on top of base configs |
debug.config |
overlay | all | Debug options; applied during CI debug runs |
ftrace.config |
overlay | all | Ftrace and tracing options; applied during CI tracing runs |
The DISCLAIMER.md file in that directory is worth reading before you use these configs in production. They are tuned for high-density ephemeral compute: they reduce virtual address space to minimize page-table size, set CONFIG_MODULES=n so the kernel cannot load drivers at runtime, and omit most peripheral subsystems entirely. They are not general-purpose Linux configs. The comment at the top of the 6.1 x86_64 config makes this explicit.
The x86_64-5.10-no-acpi config is the only config that sets CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y and # CONFIG_ACPI is not set. It exists specifically to exercise the MPTable and command-line virtio_mmio.device= discovery path, which is the legacy path Firecracker used before v1.8.0. Understanding it matters because production deployments running older guest kernels or older Firecracker versions may still be on that path.
Inside The x86_64 6.1 Config
The 6.1 x86_64 config is the currently recommended starting point. Six areas of the config are worth understanding before copying it to a new project.
Virtio
The transport and all device drivers are built in: CONFIG_VIRTIO=y, CONFIG_VIRTIO_MMIO=y, CONFIG_VIRTIO_BLK=y, CONFIG_VIRTIO_NET=y, CONFIG_VIRTIO_CONSOLE=y, CONFIG_VIRTIO_BALLOON=y, CONFIG_VIRTIO_MEM=y, CONFIG_VIRTIO_PMEM=y, CONFIG_VSOCKETS=y, CONFIG_VIRTIO_VSOCKETS=y, CONFIG_HW_RANDOM_VIRTIO=y. CONFIG_VIRTIO_PCI=y and CONFIG_VIRTIO_PCI_LEGACY=y also appear because PCI subsystem support is compiled in for ACPI, not because Firecracker exposes a PCI bus.
ACPI
CONFIG_ACPI=y is the minimum; the config adds CONFIG_ACPI_PROCESSOR=y, CONFIG_ACPI_HOTPLUG_CPU=y, CONFIG_ACPI_SLEEP=y, CONFIG_ACPI_NUMA=y, and CONFIG_ACPI_CONTAINER=y. Battery and fan ACPI drivers are explicitly disabled — there is nothing to manage.
Serial and console
CONFIG_SERIAL_8250=y and CONFIG_SERIAL_8250_CONSOLE=y set the 8250 UART as the primary boot console. CONFIG_VIRTIO_CONSOLE=y provides a second console path once virtio is initialized.
Security
The config retains the full security stack: CONFIG_SECCOMP=y, CONFIG_SECCOMP_FILTER=y, CONFIG_SECURITY_SELINUX=y, CONFIG_PAGE_TABLE_ISOLATION=y, CONFIG_RETPOLINE=y, CONFIG_STACKPROTECTOR_STRONG=y, CONFIG_HARDENED_USERCOPY=y, CONFIG_FORTIFY_SOURCE=y, CONFIG_STRICT_KERNEL_RWX=y, CONFIG_RANDOMIZE_BASE=y, CONFIG_RANDOMIZE_MEMORY=y. KASLR is enabled.
Filesystems
CONFIG_EXT4_FS=y, CONFIG_XFS_FS=y, CONFIG_SQUASHFS=y, CONFIG_OVERLAY_FS=y, and CONFIG_FUSE_FS=y are compiled in. Btrfs is disabled: CONFIG_BTRFS_FS=n.
Deliberately disabled subsystems
CONFIG_MODULES=n, CONFIG_USB=n, CONFIG_WIRELESS=n, CONFIG_BLUETOOTH=n, CONFIG_DRM=n, CONFIG_ETHERNET=n, CONFIG_ATA=n, and CONFIG_SUSPEND=n are all off. CONFIG_SUSPEND=n disables the kernel's entire suspend/resume framework and cascades to CONFIG_HIBERNATION. Neither matters in a Firecracker guest, but it does mean you cannot use these configs as a base for a kernel running on real hardware.
The aarch64 6.1 Config
The aarch64 6.1 config differs from the x86_64 6.1 config in three important ways beyond the architecture-specific drivers. It sets CONFIG_ARM64_VA_BITS_48=y, ensuring a 48-bit virtual address space. The 5.10 aarch64 config enforced 39-bit VA space to minimize page-table size, which caused a documented failure: the Go race detector on aarch64 expects a 48-bit VA space and produced spurious failures inside Firecracker guests (tracked as Firecracker issue #3514). The 6.1 config corrects this. It also sets CONFIG_ACPI_REDUCED_HARDWARE_ONLY=y for Hardware-Reduced ACPI mode and omits CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES entirely, because device-tree-based discovery is used for aarch64.
Building The Kernel
The commands in this section compile a kernel. They run entirely on the host without root privileges. No
/dev/kvmaccess is required during compilation. Themake olddefconfigstep modifies the.configfile in the source tree, and themakestep writes output under the source directory. No host-system files are modified.
x86_64
git clone --depth=1 -b v6.1 https://github.com/torvalds/linux
cd linux
cp /path/to/microvm-kernel-ci-x86_64-6.1.config .config
make olddefconfig
make vmlinux -j$(nproc)
v6.1 is the initial release tag. For production use, substitute the latest stable 6.1.x point release — check https://www.kernel.org for the current tag (e.g. v6.1.130).
make olddefconfig resolves any Kconfig symbols present in the kernel source tree but absent from the config file, filling them with their default values. Running it after placing the CI config ensures the build succeeds even if you are using a kernel point release that added new symbols.
The output artifact is the uncompressed ELF file vmlinux at the root of the source tree. Firecracker on x86_64 requires the uncompressed ELF — it reads the ELF headers directly to determine the kernel entry point and load address. Do not pass arch/x86/boot/bzImage; Firecracker will reject it. The vmlinux file is typically 25–40 MiB unstripped.
aarch64 (cross-compiled from an x86_64 host)
git clone --depth=1 -b v6.1 https://github.com/torvalds/linux
# substitute the latest 6.1.x stable tag from https://www.kernel.org
cd linux
cp /path/to/microvm-kernel-ci-aarch64-6.1.config .config
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make olddefconfig
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make Image -j$(nproc)
The aarch64-linux-gnu- toolchain is available as gcc-aarch64-linux-gnu on Debian/Ubuntu and cross-gcc-aarch64 or aarch64-linux-gnu-gcc via Homebrew on macOS.
The output artifact is arch/arm64/boot/Image, a PE-format image that Firecracker loads as an EFI stub. On aarch64, pass this path to Firecracker's kernel_image_path field. Do not pass vmlinux; Firecracker on aarch64 does not read the ELF format.
The Virtio-MMIO Transport And Device Discovery
Firecracker uses the virtio-MMIO transport defined in virtio 1.2 spec §4.2, not virtio-PCI. Both the driver requirements and the device discovery mechanism flow directly from that transport design. Each device occupies a region of guest physical memory with a fixed register layout. The first two registers the driver must verify are:
| Offset | Register | Required value |
|---|---|---|
0x000 |
MagicValue | 0x74726976 ("virt" in ASCII, little-endian) |
0x004 |
Version | 2 (virtio 1.x devices); 1 is the legacy interface |
0x008 |
DeviceID | Device type (2 = block, 1 = net) |
0x070 |
Status | Device status register; driver writes initialization sequence here |
The virtio 1.2 spec §4.2.2 states that drivers MUST verify both MagicValue and Version before proceeding; a device that returns any other value is not a virtio device and must be ignored. All registers are 32-bit little-endian.
MMIO has no generic discovery protocol: there is no BIOS that enumerates MMIO devices the way PCI enumerate cards. The guest must be told where each device lives before it can check MagicValue. Firecracker has provided three different answers to this problem at different points in its history:
On the ACPI path (current x86_64 since v1.8.0), Firecracker generates an FADT, MADT, and DSDT. The DSDT contains AML bytecode that defines each virtio device. The guest's ACPI subsystem parses the DSDT at boot and invokes the virtio_mmio driver for each device it finds. CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not required and should not be set on this path.
On the legacy x86_64 path, Firecracker appended virtio_mmio.device=<size>@<base>:<irq> to the kernel command line for each device. The microvm-kernel-ci-x86_64-5.10-no-acpi.config CI config exists specifically to test this path.
On aarch64, the device tree provided by Firecracker describes each device's MMIO base and IRQ. No command-line stanzas and no CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES are needed.
The Kernel Command Line
Firecracker's documented default kernel command line is:
reboot=k panic=1 nomodule 8250.nr_uarts=0 i8042.noaux i8042.nomux i8042.dumbkbd swiotlb=noforce
Each parameter earns its place. reboot=k tells Linux to perform a keyboard-controller (i8042) reset on reboot; that is the only reboot path Firecracker emulates, so any other reboot method will hang the guest. panic=1 causes an automatic reboot one second after a kernel panic, rather than halting indefinitely. nomodule disables module loading at the command-line level, matching the CONFIG_MODULES=n compile-time setting. 8250.nr_uarts=0 disables the 8250 driver's automatic probing of all potential UART addresses — Firecracker provides exactly one UART and the guest must not probe for more. The three i8042.* parameters put the i8042 controller into a mode that matches what Firecracker emulates: no auxiliary port (mouse), no multiplexing, dumb keyboard mode. swiotlb=noforce disables the software I/O TLB bounce buffer, which is unnecessary when EPT or NPT is available and adds latency.
The getting-started guide uses a simpler line for its worked example:
console=ttyS0 reboot=k panic=1
console=ttyS0 directs kernel log output to the first 8250 UART. On aarch64, the guide prepends keep_bootcon to preserve early console output through the console switch.
Constructing The Root Filesystem
The rootfs Firecracker boots is a regular ext4 image file on the host. Firecracker maps it through its drive API as a virtio-block device (virtio 1.2 §5.2, Device ID 2) and the guest kernel mounts it as the root filesystem. There is no initramfs step in the typical Firecracker setup: the kernel mounts the ext4 image directly.
What The ext4 Image Must Be
The ext4 superblock carries a magic number, 0xEF53, at offset 0x38 from the start of the superblock (which begins at byte 1024 of the image). The s_rev_level field at offset 0x4C must be 1 (EXT4_DYNAMIC_REV) for modern ext4 features including variable inode sizes. The s_inode_size field at offset 0x58 encodes the inode size; the default is 256 bytes. Block size is 2^(10 + s_log_block_size); the default is 4 KiB, which matches the 4K page size the CI kernel configs require.
These are details you do not normally need to track because mkfs.ext4 sets them correctly by default. They matter when verifying that a pre-built image is a valid ext4 volume before trying to boot it.
Creating And Populating The Image
The
mountcommand below requires root. It modifies the kernel's mount table and grants direct device access to a loop device. An alternative usingmke2fs -dthat avoids root entirely is shown afterward.
The sequence from the official docs/rootfs-and-kernel-setup.md:
dd if=/dev/zero of=rootfs.ext4 bs=1M count=50
mkfs.ext4 rootfs.ext4
sudo mount rootfs.ext4 /tmp/my-rootfs
# populate /tmp/my-rootfs
sudo umount /tmp/my-rootfs
Fifty mebibytes is the documented example size. mkfs.ext4 with no additional flags produces a correct ext4 filesystem for Firecracker.
If you cannot run as root during image construction, mke2fs accepts a -d rootdir argument that copies a directory tree into the new filesystem without requiring a mount:
mke2fs -t ext4 -F -d /path/to/rootfs-tree rootfs.ext4 50M
-F forces creation on a regular file rather than a block device. The -d argument accepts either a directory path or a path to an uncompressed tar archive. This is the cleaner path in CI environments where root is unavailable.
The Alpine Linux / OpenRC Approach
The official Firecracker documentation describes a Docker-chroot workflow using Alpine Linux and OpenRC:
dd if=/dev/zero of=rootfs.ext4 bs=1M count=50
mkfs.ext4 rootfs.ext4
sudo mount rootfs.ext4 /tmp/my-rootfs
docker run -it --rm -v /tmp/my-rootfs:/my-rootfs alpine
Inside the Alpine container:
apk add openrc util-linux
ln -s agetty /etc/init.d/agetty.ttyS0
echo ttyS0 > /etc/securetty
rc-update add agetty.ttyS0 default
rc-update add devfs boot
rc-update add procfs boot
rc-update add sysfs boot
for d in bin etc lib root sbin usr; do tar c "/$d" | tar x -C /my-rootfs; done
for dir in dev proc run sys var; do mkdir /my-rootfs/${dir}; done
OpenRC is the recommended init system for this workflow, not BusyBox init. The rc-update commands register agetty on ttyS0 so a login prompt appears on the serial console, and register the virtual filesystem mounts (devfs, procfs, sysfs) so the kernel's device and process views are available at runtime. The for loop copies the Alpine filesystem contents out of the container and into the ext4 image.
What Init The Kernel Will Find
When the kernel finishes mounting the root filesystem, it executes PID 1. The search order is fixed in Linux v6.1 init/main.c, in the function kernel_init() at lines 1035–1110, with the /sbin/init fallback sequence at lines 1103–1105:
/init— the initramfs path;ramdisk_execute_commanddefaults to"/init"(declared at line 152)- The value of the
init=kernel parameter, if set CONFIG_DEFAULT_INIT, if set at compile time/sbin/init/etc/init/bin/init/bin/sh
If all seven attempts fail, the kernel panics: Kernel panic - not syncing: No working init found. The rdinit= parameter overrides /init for initramfs; init= overrides the fallback sequence for the final root. Passing init=/bin/sh on the Firecracker boot parameters is the fastest way to get a working shell in a rootfs that lacks a real init system.
BusyBox init is a viable minimal init for a Firecracker guest, with one thing to know: BusyBox init does not implement SysV runlevels. The runlevels field in /etc/inittab entries is completely ignored. Without any /etc/inittab, BusyBox init runs its compiled-in defaults: sysinit scripts, askfirst on recognized console devices, ctrlaltdel, and shutdown scripts. A minimal BusyBox system needs only /dev, /etc, the BusyBox binary itself, and a kernel.
The Minimum Directory Structure
The minimum rootfs that will boot is:
/sbin/init (or /init, or something at init= on the cmdline)
/dev/ (empty; kernel mounts devtmpfs or populates via mdev)
/proc/ (empty; procfs mount point)
/sys/ (empty; sysfs mount point)
The Alpine example also creates /run and /var, which OpenRC requires. Without /proc and /sys as mount points, many userspace tools fail immediately after boot because they cannot read kernel state.
Attaching The Drive
The Firecracker REST API attaches the rootfs image through the PUT /drives/{drive_id} endpoint. The minimal JSON body:
{
"drive_id": "rootfs",
"path_on_host": "/path/to/rootfs.ext4",
"is_root_device": true,
"is_read_only": false
}
The full drive schema (src/firecracker/swagger/firecracker.yaml) supports additional fields worth knowing:
cache_type controls whether Firecracker advertises VIRTIO_BLK_F_FLUSH (feature bit 9 in virtio 1.2 §5.2.3). With "Unsafe" (the default), the flag is not advertised and the guest cannot issue flush commands. With "Writeback", the flag is advertised and Firecracker calls fsync() on the backing file when the guest sends a flush request.
io_engine selects between "Sync" (the default, using pread/pwrite) and "Async" (using io_uring). The async engine requires the host kernel to be at least version 5.10.51. It adds approximately 110 ms of device creation latency. For read workloads on NVMe storage where data is not in the host page cache, the Firecracker block I/O documentation reports 1.5x–3x I/O efficiency improvement and up to 30x total IOPS; write workloads see 20–45% IOPS improvement but lower per-CPU efficiency than the Sync engine. It is marked as developer preview.
partuuid is meaningful only when is_root_device is true. It specifies a partition UUID that becomes the root=PARTUUID=... value appended to the kernel command line, useful when the ext4 image contains a partition table rather than a bare filesystem.
Sources And Further Reading
- Firecracker kernel policy (supported versions, required CONFIG options, 4K page size, default command line): https://github.com/firecracker-microvm/firecracker/blob/main/docs/kernel-policy.md
- Firecracker CHANGELOG (per-version feature additions: ACPI in v1.8.0, balloon in v1.4.0, v5.10 EOL, v4.14 removal): https://github.com/firecracker-microvm/firecracker/blob/main/CHANGELOG.md
- CI kernel config files directory: https://github.com/firecracker-microvm/firecracker/tree/main/resources/guest_configs
- CI config DISCLAIMER (rationale for VA space reduction, no modules, stripped drivers): https://raw.githubusercontent.com/firecracker-microvm/firecracker/main/resources/guest_configs/DISCLAIMER.md
- x86_64 6.1 CI config: https://github.com/firecracker-microvm/firecracker/blob/main/resources/guest_configs/microvm-kernel-ci-x86_64-6.1.config
- aarch64 6.1 CI config: https://raw.githubusercontent.com/firecracker-microvm/firecracker/main/resources/guest_configs/microvm-kernel-ci-aarch64-6.1.config
- Official rootfs and kernel build guide (
vmlinuxvsImage,dd/mkfs/mountsequence): https://github.com/firecracker-microvm/firecracker/blob/main/docs/rootfs-and-kernel-setup.md - Getting-started guide (drive JSON, kernel command line examples, Alpine/OpenRC workflow): https://github.com/firecracker-microvm/firecracker/blob/main/docs/getting-started.md
- Firecracker REST API schema (Drive object, all fields and types): https://github.com/firecracker-microvm/firecracker/blob/main/src/firecracker/swagger/firecracker.yaml
- Block caching documentation (
cache_typesemantics,VIRTIO_BLK_F_FLUSH,fsync): https://github.com/firecracker-microvm/firecracker/blob/main/docs/api_requests/block-caching.md - Block I/O engine documentation (
io_uring, host kernel requirement, IOPS figures): https://github.com/firecracker-microvm/firecracker/blob/main/docs/api_requests/block-io-engine.md - Firecracker FAQ (no ACPI power management,
reboot=k/ i8042 reboot path): https://github.com/firecracker-microvm/firecracker/blob/main/FAQ.md - PR #4428 merging ACPI support (merged 2024-03-25, released in v1.8.0): https://github.com/firecracker-microvm/firecracker/pull/4428
- Original virtio-MMIO cmdline vs ACPI discovery issue: https://github.com/firecracker-microvm/firecracker/issues/2519
- OASIS virtio 1.2 Committee Specification 01 (§4.2.2 MMIO register layout; §5.2 block device feature bits, config space, request struct): https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html
mke2fs(8)man page (-d rootdir,-F, block size heuristics): https://man7.org/linux/man-pages/man8/mke2fs.8.html- ext4 superblock layout (magic
0xEF53at offset0x38, inode size, block size formula): https://docs.kernel.org/next/filesystems/ext4/super.html - Linux v6.1
init/main.c(ramdisk_execute_commandat line 152;kernel_init()at lines 1035–1110;/sbin/initfallback at lines 1103–1105): https://raw.githubusercontent.com/torvalds/linux/v6.1/init/main.c - Kernel documentation on ramfs, rootfs, and initramfs (
init=vsrdinit=, init search order): https://docs.kernel.org/filesystems/ramfs-rootfs-initramfs.html - BusyBox init (no SysV runlevels;
/etc/inittabrunlevels field ignored): https://busybox.net/BusyBox.html