How a MicroVM Boots in Under a Second

Traditional VMs take thirty seconds. Firecracker takes 125 milliseconds. The difference is everything it refuses to do.

Share
How a MicroVM Boots in Under a Second. Abstract deep dive illustration in orange and dark grey on debugly.dev

Disclosure: I run Krova Cloud, which is built on Firecracker. This post is about the mechanism, and everything here is verifiable against the open source project and against the boot times we run in production.

A traditional virtual machine takes twenty to sixty seconds to boot. A Firecracker microVM boots in roughly 125 milliseconds.

That is not an optimisation. You do not get a 200x improvement by tuning. You get it by removing entire categories of work, and the interesting part is what turns out to be removable.

What a normal VM spends its time on

Boot a standard QEMU VM with a general purpose kernel and the sequence is roughly:

  1. Firmware. BIOS or UEFI initialises, runs self tests, enumerates devices, presents a boot menu that nobody uses. Several seconds.
  2. Bootloader. GRUB loads, reads its config, possibly waits on a timeout, loads the kernel and initramfs.
  3. Kernel init. Decompresses, initialises subsystems, then probes for hardware. PCI enumeration, ACPI tables, USB controllers, SATA, graphics.
  4. initramfs. A small root filesystem loads modules needed to mount the real root.
  5. init system. systemd starts, parses unit files, resolves a dependency graph, starts dozens of services: logging, networking, cron, dbus, ssh.

Steps one, three, and five dominate. And nearly all of that work exists to handle hardware diversity and general purpose use cases that do not apply when the machine is virtual and single purpose.

What Firecracker removes

No firmware, no bootloader

Firecracker loads the kernel directly into guest memory and jumps to its entry point. There is no BIOS and no GRUB.

It uses the Linux boot protocol, setting up the boot parameters structure in memory and starting the vCPU at the 64 bit entry point. The kernel starts executing immediately.

That deletes the first two phases entirely, which on a normal VM is most of the wall clock time before the kernel even starts.

Almost no devices

This is the big one. A general purpose hypervisor emulates a plausible PC: PCI bus, IDE and SATA controllers, USB, VGA, sound, floppy. The guest kernel probes all of it at boot, and probing is slow because it involves timeouts on hardware that is not there.

Firecracker's device model is deliberately tiny:

Device Purpose
virtio-block Disk
virtio-net Network
virtio-vsock Host and guest communication
serial console Output
i8042 controller One key, for reset

That is close to the complete list. No PCI bus at all, since devices are placed at fixed MMIO addresses. No ACPI in the traditional sense. No graphics.

The guest kernel finds its handful of devices immediately and skips the rest, because there is nothing to enumerate.

A minimal guest kernel

You compile a kernel with only what you need. No drivers for hardware that cannot exist, no filesystems you will not mount, no subsystems you will not use.

A tuned microVM kernel config is a few megabytes and initialises in tens of milliseconds. It also boots without an initramfs, since the virtio block device driver is compiled in rather than loaded as a module, so the kernel can mount the root filesystem directly.

A minimal init

If your workload is one process, init can be that process. No systemd, no service dependency graph, no journal.

For a sandbox that runs one command and exits, this is entirely reasonable, and it removes the last multi-second chunk.

Where the remaining time goes

Roughly, on a modern host:

Phase Time
Firecracker process start, KVM setup ~5ms
Guest memory allocation and kernel load ~15ms
vCPU start to kernel entry ~2ms
Kernel init ~50ms
Userspace init to ready ~50ms

The VMM itself is a small fraction. Most of the remaining time is the guest kernel doing genuinely necessary work.

Firecracker is written in Rust and the process is small, a few megabytes of overhead per VM, which matters as much as boot time when you are running thousands of them on one host.

Snapshots make it faster still

The technique that gets you below the boot time floor: boot once, snapshot the memory and device state, then restore from the snapshot instead of booting.

firecracker --api-sock /tmp/fc.sock
# PUT /snapshot/create   -> memory file + state file
# PUT /snapshot/load     -> restore

Restore can be tens of milliseconds, because the guest never boots. Its memory is mapped from a file and the vCPU resumes at exactly the instruction where the snapshot was taken.

Combined with copy on write page mapping, one snapshot can be the base for many VMs, each only allocating pages it actually writes. That is how you get very high density with fast start times.

There are real caveats and they matter:

Entropy. A restored VM has the same random state as every other restore of that snapshot. Any long lived key generated after restore is compromised. The guest needs to reseed, and Linux has a virtio-rng notification mechanism for exactly this.

Time. The guest clock is frozen at snapshot time and wakes up believing it is the past. TLS certificate validation fails, tokens look valid when they are expired. The guest needs to resync on resume.

Network state. Open connections in the snapshot are dead on restore. The peer has long since forgotten them.

Every one of these is a correctness problem that only appears in production, and they are the reason snapshot restore is more work to operate than it looks in a benchmark.

Why the small device model is also a security property

The performance story and the isolation story are the same story.

A hypervisor's attack surface is its device emulation, because that is the code processing input from an untrusted guest. Historic VM escapes have overwhelmingly been bugs in emulated devices, and the famous ones were in things like floppy controllers and graphics adapters, which nobody was using.

Five simple virtio devices is a much smaller surface than a full PC emulation. Firecracker also runs each VM in a jailer that applies a seccomp filter, a chroot, and a dedicated uid, so even a VMM compromise lands in a sandbox rather than on the host.

Compare to a container, where the guest talks directly to a shared kernel across roughly 350 syscalls. The surface is orders of magnitude different, and that difference is why this architecture exists.

What you give up

Being honest about the trade, because this is not free.

Memory is reserved, not shared. Containers share page cache and can overcommit. Each microVM gets its own allocation. Density is lower and more predictable.

Startup is still slower than a container. 125ms against 20ms. If you start one per request, that gap matters.

No hardware passthrough in the common configurations. No GPU in most setups, which rules out a lot of ML work.

Your own kernel to maintain. A minimal custom kernel is a thing you now own, including its security updates.

Anything expecting a full PC will be confused. Software that probes for ACPI tables or PCI devices finds nothing.

For running your own trusted services, a container remains the right default and the isolation is not worth the overhead. The case for a microVM is when the code is not yours, which is the line I would draw for agent sandboxes and multi tenant platforms.

Why this is interesting beyond microVMs

The generalisable lesson is about where boot time actually goes.

Nearly all of a traditional VM's startup is spent handling generality: hardware that might exist, configurations that might apply, services that might be needed. Remove the generality and the same fundamental operation is 200 times faster.

That pattern shows up constantly. A build that takes four minutes is usually not doing four minutes of compilation, it is doing thirty seconds of compilation and three and a half minutes of checking whether things changed. A page that takes nine seconds is rarely nine seconds of useful work.

The question worth asking of any slow system is not "how do I make this faster" but "what is it doing that it does not need to do". Firecracker's answer to that question happened to be almost everything.