Level 1: Virtualization and Firecracker Foundation
This level establishes the technical floor every other level stands on. By the end of it you will
have built Firecracker from source with tools/devtool, run both its Rust unit tests and its
Python integration suite, booted a real microVM by hand over the API socket, and — the part that
matters most — written a ~70-line standalone KVM "VMM" in Rust that opens /dev/kvm, creates a VM
and a vCPU, maps guest memory, runs the KVM_RUN loop, and handles VM exits. You will do that
before you read a single line of Firecracker's Vcpu, because you cannot understand what
Firecracker adds until you have felt what KVM gives you raw.
Firecracker is a Virtual Machine Monitor (VMM) written in Rust by AWS, licensed Apache-2.0, and built on KVM. It creates microVMs: minimal, fast-booting, low-overhead virtual machines that boot to application code in under 125 ms, add less than 5 MiB of memory overhead each, and pack thousands onto a single host. It powers AWS Lambda and AWS Fargate, and it was originally forked from Google's crosvm. Its defining design choice is to do almost nothing: where QEMU emulates hundreds of devices, a BIOS, PCI, and USB, Firecracker emulates a network device, a block device, a vsock device, a serial console, and a partial keyboard controller — and stops. That minimalism is a security argument, not an aesthetic one, and it is the through-line of this entire curriculum.
This curriculum will not hold your hand. It points you at the right parts of the codebase, gives you
the right questions to ask, and makes you run everything you read. Every time this book names a
struct, a function, or an ioctl, it also gives you the rg/grep/find that locates it on your
checkout — because code moves between branches, and a contributor who quotes memorized line numbers
is already wrong. The fact sheet behind this book is current to roughly Firecracker v1.16; wherever a
fact is version-sensitive it is marked "(verify on your branch)", and you are expected to actually
verify.
Learning Objectives
By the end of Level 1 you must be able to:
- Explain where a VMM sits in the virtualization stack — orchestrator above it, jailer wrapping it, KVM beneath it, guest inside it — and which problem each layer owns.
- Build Firecracker from source with
tools/devtool build, in both debug and--release, and locate the resultingfirecrackerandjailerbinaries by path. - Run the unit tests (
cargo test) and the pytest integration harness (tools/devtool test), scope a run to a single test, and read the output of each. - Run the style and build gates contributors must pass:
tools/devtool fmt,checkstyle,checkbuild --all, and clippy as warnings-as-errors. - Boot a microVM end to end from your own build: start the VMM on a Unix socket, configure
boot-source/drives/machine-config over
curl, issueInstanceStart, and log in over the serial console. - Map every
curl PUTin a boot sequence to the thread and the struct that will eventually handle it (the API thread, aVmmAction, the VMM thread, a device). - Write, from scratch, a minimal KVM VMM in Rust using
kvm-ioctls/kvm-bindingsthat runs real-mode code and handlesKVM_EXIT_IOandKVM_EXIT_HLT. - Locate any type named across Levels 2–9 (
Vmm,Vcpu,VmmAction,GuestMemoryMmap,MMIODeviceManager, …) in your checkout without a search engine.
The Virtualization Stack: Where Firecracker Sits
Before you touch the code, fix an accurate mental model of the layers. One Firecracker process is
exactly one microVM. It is launched (in production) by an orchestrator through the jailer,
which builds an isolation barrier and then execs the firecracker binary; that binary runs three
classes of thread; underneath it all, KVM runs the guest's code directly on the physical CPU's
virtualization extensions.
┌────────────────────────────────────────────────────────────────────────┐
│ Orchestrator (firecracker-containerd / Kata / firectl / your code) │ control plane
│ starts microVMs, talks REST over a Unix domain socket │
└────────────────────────────────────────────────────────────────────────┘
│ exec(jailer ...) │ HTTP/JSON over the UDS
▼ │
┌────────────────────────────────────────────────────────────────────────┐
│ jailer (binary) ── pivot_root + cgroups + namespaces + drop privs ──► │ the security barrier
│ then execs ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ firecracker (the VMM, one process = one microVM) │ │
│ │ • API thread HTTP server on the socket (control plane) │◄──┘
│ │ • VMM thread owns `Vmm`; runs the EventManager epoll loop; │
│ │ device emulation, MMDS, rate limiting │
│ │ • vCPU thread × N each runs the KVM_RUN loop │
│ │ seccomp-BPF filter applied per thread (vmm / api / vcpu) │
│ └──────────────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────────┘
│ ioctl() on /dev/kvm (KVM_CREATE_VM, KVM_RUN, …)
▼
┌────────────────────────────────────────────────────────────────────────┐
│ KVM (Linux kernel module) ── runs guest code on VT-x / AMD-V / ARM │ hardware virtualization
└────────────────────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────────────┐
│ The guest: an unmodified Linux kernel + your rootfs, inside a microVM │
└────────────────────────────────────────────────────────────────────────┘
You will build the bottom box — the KVM box — yourself in Lab 1.4. The threading model in the middle box is the subject of Level 3 and the threading-model deep dive; the jailer is Level 9 and the jailer deep dive. For now you only need to know the layers exist and which one owns which problem.
Note: The single hard prerequisite for everything below is a working
/dev/kvm. Firecracker is Linux-only, x86_64 or aarch64, and needs hardware virtualization. Runls -l /dev/kvmnow; if it is absent you are on the wrong host (on AWS, use a*.metalbare-metal instance — plain nested EC2 does not expose KVM).
VMM vs. Container vs. Other VMMs
You will hear "it's basically a container" and "it's basically QEMU" — both are wrong, and knowing exactly how they are wrong is part of the job. A VM is isolated by CPU virtualization extensions and runs its own kernel; a container shares the host kernel and is isolated by namespaces and cgroups. Firecracker is a VMM that deliberately looks container-shaped from the outside (fast, dense, cheap) while keeping a real VM boundary on the inside.
| Firecracker | QEMU | Cloud Hypervisor | gVisor | runc / container | |
|---|---|---|---|---|---|
| Isolation boundary | KVM microVM, minimal devices | KVM/TCG, full machine | KVM/MSHV, rust-vmm | userspace kernel (Sentry) intercepts syscalls | namespaces + cgroups (shared host kernel) |
| Own guest kernel? | Yes | Yes | Yes | Emulated in userspace | No (shares host) |
| Boot time | ≤ 125 ms | 100s ms – s | sub-second | container-class | fastest |
| Mem overhead / instance | < 5 MiB | ~131 MB | ~13 MB | tens of MB | lowest |
| Device model | virtio-mmio net/block/vsock/rng/balloon, serial, i8042-reset; no BIOS/PCI-legacy/USB | huge (PCI/USB/GPU/firmware) | moderate (virtio-pci, ACPI, hotplug, VFIO) | n/a (syscall surface) | host devices |
| Language | Rust | C | Rust | Go | Go |
| Use case | serverless multi-tenant | general virtualization | modern cloud guests | sandboxing containers | cooperative tenants |
The insight to carry forward: a VM is strong isolation because the guest runs behind VT-x/AMD-V — but the VMM is privileged host code and is itself part of the attack surface. A guest that compromises the VMM escapes the box. That is precisely why a minimal VMM in a memory-safe language matters, and why "QEMU has it" is never an argument for adding a device to Firecracker. The full table lives in Firecracker vs. Other VMMs.
Required Reading
Read these in your own checkout, in order, before the labs. In a mature project the best documentation is in-repo — treat it as primary source, not background.
| # | Resource | What to extract |
|---|---|---|
| 1 | README.md (repo root) | Scope, the elevator pitch, where the binaries live, links into docs/. |
| 2 | CONTRIBUTING.md | The fork→branch→PR flow, DCO sign-off (git commit -s), the devtool checks, the ≥2-approvals rule. You will live this in Level 2. |
| 3 | docs/getting-started.md | How to get a kernel + rootfs and boot a microVM by hand — the exact recipe for Lab 1.3. |
| 4 | docs/design.md | The architecture: threads, the API, the device model, the security model. The single most important conceptual file in this level. |
Confirm they exist on your branch (names occasionally move between major lines):
# From the repo root of your firecracker checkout:
ls README.md CONTRIBUTING.md CHANGELOG.md SPECIFICATION.md MAINTAINERS.md
ls docs/getting-started.md docs/design.md docs/jailer.md docs/seccomp.md
# If a docs path moved, find it by topic rather than trusting the name:
rg -l -i "getting started|boot.*microvm" docs/
Source Code Areas to Inspect
You are not editing anything yet — you are building a map. The src/ workspace recently went through
a large refactor that merged many formerly separate crates into one big vmm crate. The current
members:
Crate (src/<name>) | Role |
|---|---|
vmm | The core VMM library: machine model, vCPU/KVM state, all device emulation, snapshots, MMDS, rate limiting, seccomp, logging. The big merged crate — you will live here. |
firecracker | The firecracker binary and the HTTP API server (src/firecracker/src/api_server/). Drives vmm. |
jailer | The jailer binary — sets up the isolation barrier, then execs firecracker. |
seccompiler | Compiles JSON seccomp filters → BPF (rust-vmm's external seccompiler originated here). |
cpu-template-helper | Create/inspect/verify CPU templates. |
snapshot-editor | Inspect/edit snapshot files. |
rebase-snap | Rebase a diff snapshot's memory onto a base. |
acpi-tables | Build guest ACPI tables. |
utils | Internal shared utilities. |
Inside src/vmm/src/ the modules that were historically separate crates are the ones you will
return to most:
| Module path | Why |
|---|---|
vstate/ | vCPU/VM/KVM state: vstate/vcpu/, vstate/vm.rs, vstate/memory.rs. The KVM-facing core. |
devices/virtio/{block,net,vsock,balloon,rng,...} | The in-tree virtio device implementations. |
arch/{x86_64,aarch64}/ | Boot protocol, memory layout, register setup — architecture-specific. |
device_manager/ | MMIODeviceManager + PortIODeviceManager (x86) + ACPIDeviceManager. |
builder.rs, resources.rs, rpc_interface.rs | microVM construction, aggregated config, the VmmAction enum. |
mmds/, dumbo/, rate_limiter/ | Metadata service, its tiny TCP/IP stack, the token-bucket limiter. |
Tip:
vmm-sys-utilandseccompilerare now external rust-vmm dependencies, not vendored in-tree. Ifrgfor them insidesrc/comes up empty, that is expected — look inCargo.tomlinstead. Paths drift; always locate byrg/find, never by remembered line number.
Key Types Quick Reference
Memorize the role of each. By the end of Level 1 you should be able to predict the file path of any of these before running the command that confirms it.
| Type | Crate / module | Role | Find it |
|---|---|---|---|
Vmm | vmm · src/vmm/src/lib.rs | The running microVM object; owned by the VMM thread. | rg -n "pub struct Vmm" src/vmm/src/ |
Vcpu / KvmVcpu | vmm · vstate/vcpu/ | A virtual CPU; runs the KVM_RUN loop in its own thread. | rg -n "struct Vcpu\b|struct KvmVcpu" src/vmm/src/vstate/ |
VmmAction | vmm · rpc_interface.rs | Control-plane command enum sent API thread → VMM thread. | rg -n "enum VmmAction" src/vmm/src/ |
VmResources | vmm · resources.rs | Aggregated pre-boot configuration of the microVM. | rg -n "struct VmResources" src/vmm/src/resources.rs |
build_microvm_for_boot | vmm · builder.rs | Constructs the Vmm from VmResources at boot. | rg -n "fn build_microvm_for_boot|fn build_and_boot_microvm" src/vmm/src/builder.rs |
MMIODeviceManager | vmm · device_manager/ | Places and dispatches virtio-MMIO devices on the bus. | rg -n "struct MMIODeviceManager" src/vmm/src/device_manager/ |
GuestMemoryMmap | rust-vmm vm-memory | The guest's RAM, host-mmap-backed, addressed by GuestAddress. | rg -n "GuestMemoryMmap" src/vmm/src/vstate/memory.rs |
ApiServer | firecracker · api_server/ | The HTTP server in the API thread. | rg -n "struct ApiServer" src/firecracker/src/ |
EventManager | rust-vmm event-manager | The epoll loop the VMM thread runs. | rg -n "EventManager" src/vmm/src/ | head |
Build the muscle memory now — predict each path, then run:
# From the repo root. You should be able to guess the directory before each result appears.
rg -n "pub struct Vmm\b" src/vmm/src/lib.rs
rg -n "enum VmmAction" src/vmm/src/rpc_interface.rs
rg -n "struct VmResources" src/vmm/src/resources.rs
rg -n "fn build_microvm_for_boot" src/vmm/src/builder.rs
rg -n "struct MMIODeviceManager" src/vmm/src/device_manager/
Note: Some of these names are version-sensitive —
build_and_boot_microvmvs.build_microvm_for_boot, the exactvstatesub-paths, and whetherApiServerlives underapi_server/or has been renamed are all things to verify on your branch. If a command returns nothing, broaden it (drop the trailing path, search the wholesrc/) rather than assuming the type is gone.
GitHub Issue Categories for Level 1 Contributors
Firecracker tracks everything on GitHub. At this stage you are learning the machine, not changing it — restrict yourself to issues that exercise the workflow and your understanding, not the VMM core:
good first issue— curated, scoped, beginner-appropriate. Always start here.- Documentation — wrong/stale instructions in
docs/, brokengetting-startedsteps, outdated version strings, incorrectcurlexamples. - Build / dev-environment — a
tools/devtoolrough edge, a missing prerequisite in a doc, a toolchain mismatch you hit and can reproduce.
How to find them (the gh CLI, or the label-filtered web lists):
gh issue list --repo firecracker-microvm/firecracker \
--label "Good first issue" --state open --limit 30
# Browse: https://github.com/firecracker-microvm/firecracker/labels
# Labels you will see: "Good first issue", "Type: Bug", "Type: Enhancement",
# "Status: Awaiting review", "Priority: ...", "Kani" (formal verification), "Roadmap: ..."
Warning: Read the entire comment thread before touching an issue. If it has an assignee or an open linked PR, move on. Comment that you intend to work on it first. Etiquette is covered in depth in Community Interaction. You do not open a PR in Level 1 — that starts in Level 2.
Deliverables
Demonstrate all of the following before advancing to Level 2:
-
A successful
tools/devtool build(debug) andtools/devtool build --release, with thefirecrackerbinary located by path (Lab 1.1). -
The unit tests pass (
cargo test) and at least one targeted pytest runs green viatools/devtool test -- -k ...(Lab 1.2). -
tools/devtool checkstyleand clippy (-D warnings) pass clean on an unmodified checkout (Lab 1.2). - A microVM booted from your own build to a login prompt over the serial console, configured by hand over the API socket (Lab 1.3).
-
A standalone Rust KVM VMM you wrote that runs real-mode code and prints output via
KVM_EXIT_IO, plus the mapping table from your primitives to Firecracker's types (Lab 1.4). -
The ability to name the file path of
Vmm,Vcpu,VmmAction, andGuestMemoryMmapfrom memory, and to explain in two sentences what Firecracker adds on top of bare KVM.
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Trying to build on macOS / Windows / a non-KVM VM | Nothing works; confusing errors | Firecracker is Linux-only and needs /dev/kvm. Use bare metal or a host with nested virt. |
Installing a system Rust and ignoring rust-toolchain.toml | Subtle build/clippy diffs from CI | The dev container pins the channel; let tools/devtool manage it. |
Running cargo build for the gnu target by habit | Binary won't match CI artifacts; link surprises | Default target is *-musl; build via devtool unless you have a reason. |
Expecting cargo test to run the integration suite | "Where are the real tests?" | The integration suite is pytest in tests/, driven by tools/devtool test. |
firecracker --help and looking for a "run" subcommand | There isn't one | You configure a microVM over the API socket (or --config-file), then InstanceStart. |
| Quoting a struct's line number from this book | Wrong on your branch | This book never gives line numbers. Locate with the provided rg/find. |
| Reading the run loop before doing Lab 1.4 | Abstract understanding that collapses under debugging | Build the bare-KVM VMM first; then Firecracker's Vcpu reads as "the same, hardened." |
How to Verify Success
# 1. Built binaries exist (adjust arch to your host: x86_64 or aarch64).
ARCH=$(uname -m)
ls -l build/cargo_target/${ARCH}-unknown-linux-musl/debug/firecracker
ls -l build/cargo_target/${ARCH}-unknown-linux-musl/release/firecracker
./build/cargo_target/${ARCH}-unknown-linux-musl/release/firecracker --version
# 2. Unit tests + style gates pass.
tools/devtool test -- integration_tests/build/test_unittests.py # or: cargo test in the container
tools/devtool checkstyle
# 3. A microVM boots from your build (Lab 1.3) — you reach a guest login prompt.
# 4. Your hand-written KVM VMM (Lab 1.4) prints, e.g.:
# Hello from a guest! ← bytes emitted via KVM_EXIT_IO
# KVM_EXIT_HLT, stopping.
PR Profile: Level 1 Graduate
A Level 1 graduate can credibly open these kinds of PRs. Scope is everything; this graduate has not yet touched the VMM core.
| PR type | Example | Test requirement |
|---|---|---|
| Documentation fix | Correct a stale step in docs/getting-started.md; fix a wrong curl example | None — docs only; manual verification |
| Build/dev ergonomics | Fix a misleading error or missing prerequisite note in a tools/devtool path | Reproduce locally; show before/after |
| Comment / wording | Clarify a confusing doc-comment you had to decode while reading | Compiles clean; checkstyle passes |
| Version-string / changelog hygiene | Correct an outdated version reference in an in-repo doc | Manual verification |
You are not yet ready to submit: API validation changes, new device config, vCPU/KVM changes, or anything touching the wire protocol or snapshot format. The contribution workflow (DCO, CHANGELOG, the devtool gates, your first real fix) starts in Level 2.