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:

  1. 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.
  2. Build Firecracker from source with tools/devtool build, in both debug and --release, and locate the resulting firecracker and jailer binaries by path.
  3. 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.
  4. Run the style and build gates contributors must pass: tools/devtool fmt, checkstyle, checkbuild --all, and clippy as warnings-as-errors.
  5. 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, issue InstanceStart, and log in over the serial console.
  6. Map every curl PUT in a boot sequence to the thread and the struct that will eventually handle it (the API thread, a VmmAction, the VMM thread, a device).
  7. Write, from scratch, a minimal KVM VMM in Rust using kvm-ioctls/kvm-bindings that runs real-mode code and handles KVM_EXIT_IO and KVM_EXIT_HLT.
  8. 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. Run ls -l /dev/kvm now; if it is absent you are on the wrong host (on AWS, use a *.metal bare-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.

FirecrackerQEMUCloud HypervisorgVisorrunc / container
Isolation boundaryKVM microVM, minimal devicesKVM/TCG, full machineKVM/MSHV, rust-vmmuserspace kernel (Sentry) intercepts syscallsnamespaces + cgroups (shared host kernel)
Own guest kernel?YesYesYesEmulated in userspaceNo (shares host)
Boot time≤ 125 ms100s ms – ssub-secondcontainer-classfastest
Mem overhead / instance< 5 MiB~131 MB~13 MBtens of MBlowest
Device modelvirtio-mmio net/block/vsock/rng/balloon, serial, i8042-reset; no BIOS/PCI-legacy/USBhuge (PCI/USB/GPU/firmware)moderate (virtio-pci, ACPI, hotplug, VFIO)n/a (syscall surface)host devices
LanguageRustCRustGoGo
Use caseserverless multi-tenantgeneral virtualizationmodern cloud guestssandboxing containerscooperative 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.

#ResourceWhat to extract
1README.md (repo root)Scope, the elevator pitch, where the binaries live, links into docs/.
2CONTRIBUTING.mdThe fork→branch→PR flow, DCO sign-off (git commit -s), the devtool checks, the ≥2-approvals rule. You will live this in Level 2.
3docs/getting-started.mdHow to get a kernel + rootfs and boot a microVM by hand — the exact recipe for Lab 1.3.
4docs/design.mdThe 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
vmmThe 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.
firecrackerThe firecracker binary and the HTTP API server (src/firecracker/src/api_server/). Drives vmm.
jailerThe jailer binary — sets up the isolation barrier, then execs firecracker.
seccompilerCompiles JSON seccomp filters → BPF (rust-vmm's external seccompiler originated here).
cpu-template-helperCreate/inspect/verify CPU templates.
snapshot-editorInspect/edit snapshot files.
rebase-snapRebase a diff snapshot's memory onto a base.
acpi-tablesBuild guest ACPI tables.
utilsInternal shared utilities.

Inside src/vmm/src/ the modules that were historically separate crates are the ones you will return to most:

Module pathWhy
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.rsmicroVM construction, aggregated config, the VmmAction enum.
mmds/, dumbo/, rate_limiter/Metadata service, its tiny TCP/IP stack, the token-bucket limiter.

Tip: vmm-sys-util and seccompiler are now external rust-vmm dependencies, not vendored in-tree. If rg for them inside src/ comes up empty, that is expected — look in Cargo.toml instead. Paths drift; always locate by rg/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.

TypeCrate / moduleRoleFind it
Vmmvmm · src/vmm/src/lib.rsThe running microVM object; owned by the VMM thread.rg -n "pub struct Vmm" src/vmm/src/
Vcpu / KvmVcpuvmm · 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/
VmmActionvmm · rpc_interface.rsControl-plane command enum sent API thread → VMM thread.rg -n "enum VmmAction" src/vmm/src/
VmResourcesvmm · resources.rsAggregated pre-boot configuration of the microVM.rg -n "struct VmResources" src/vmm/src/resources.rs
build_microvm_for_bootvmm · builder.rsConstructs the Vmm from VmResources at boot.rg -n "fn build_microvm_for_boot|fn build_and_boot_microvm" src/vmm/src/builder.rs
MMIODeviceManagervmm · device_manager/Places and dispatches virtio-MMIO devices on the bus.rg -n "struct MMIODeviceManager" src/vmm/src/device_manager/
GuestMemoryMmaprust-vmm vm-memoryThe guest's RAM, host-mmap-backed, addressed by GuestAddress.rg -n "GuestMemoryMmap" src/vmm/src/vstate/memory.rs
ApiServerfirecracker · api_server/The HTTP server in the API thread.rg -n "struct ApiServer" src/firecracker/src/
EventManagerrust-vmm event-managerThe 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_microvm vs. build_microvm_for_boot, the exact vstate sub-paths, and whether ApiServer lives under api_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 whole src/) 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/, broken getting-started steps, outdated version strings, incorrect curl examples.
  • Build / dev-environment — a tools/devtool rough 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) and tools/devtool build --release, with the firecracker binary located by path (Lab 1.1).
  • The unit tests pass (cargo test) and at least one targeted pytest runs green via tools/devtool test -- -k ... (Lab 1.2).
  • tools/devtool checkstyle and 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, and GuestMemoryMmap from memory, and to explain in two sentences what Firecracker adds on top of bare KVM.

Common Mistakes

MistakeConsequenceFix
Trying to build on macOS / Windows / a non-KVM VMNothing works; confusing errorsFirecracker is Linux-only and needs /dev/kvm. Use bare metal or a host with nested virt.
Installing a system Rust and ignoring rust-toolchain.tomlSubtle build/clippy diffs from CIThe dev container pins the channel; let tools/devtool manage it.
Running cargo build for the gnu target by habitBinary won't match CI artifacts; link surprisesDefault 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" subcommandThere isn't oneYou configure a microVM over the API socket (or --config-file), then InstanceStart.
Quoting a struct's line number from this bookWrong on your branchThis book never gives line numbers. Locate with the provided rg/find.
Reading the run loop before doing Lab 1.4Abstract understanding that collapses under debuggingBuild 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 typeExampleTest requirement
Documentation fixCorrect a stale step in docs/getting-started.md; fix a wrong curl exampleNone — docs only; manual verification
Build/dev ergonomicsFix a misleading error or missing prerequisite note in a tools/devtool pathReproduce locally; show before/after
Comment / wordingClarify a confusing doc-comment you had to decode while readingCompiles clean; checkstyle passes
Version-string / changelog hygieneCorrect an outdated version reference in an in-repo docManual 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.


Next: Lab 1.1 — Build Firecracker from Source.