Milestones & Checkpoints
These are the gates. The 16-week plan paces you toward them, but the calendar does not decide whether you advance — these milestones do. Each one (M1–M9) aligns to a level and certifies a specific competence. You pass a milestone when you can answer its self-checks without notes and could plausibly score the full 20 points on its rubric. If you cannot, you are not ready for the next level, regardless of what week it is.
This curriculum will not hold your hand here either. The self-check questions have real answers in the source; the rubric describes what demonstrated competence looks like, not what reading looks like. Be honest. The point of a gate is to fail you when you are not ready, so the next level does not collapse under you.
How to use this page. After finishing a level, sit down without the book open and answer that milestone's questions out loud or in writing. Then score yourself against the 20-point rubric (four dimensions × 5 points). A passing score is ≥16/20 with no dimension below 3. Anything less: re-do the weakest labs and the deep dives they reference, then re-test.
flowchart LR
M1[M1: Build & Boot] --> M2[M2: Contributor Workflow]
M2 --> M3[M3: Architecture & Threads]
M3 --> M4[M4: KVM & the Run Loop]
M4 --> M5[M5: Testing & Debugging]
M5 --> M6[M6: Boot & Guest Memory]
M6 --> M7[M7: Virtio Device Model]
M7 --> M8[M8: Real Contribution]
M8 --> M9[M9: Advanced Maintainer]
M9 --> CAP([Capstone])
| Milestone | Level | Certifies |
|---|---|---|
| M1 | Level 1 | Build, test, and boot Firecracker; KVM from scratch. |
| M2 | Level 2 | The GitHub/DCO contribution workflow; a clean first PR. |
| M3 | Level 3 | The threading model and API→VMM action channel. |
| M4 | Level 4 | KVM ioctls, the vCPU run loop, and VM exits. |
| M5 | Level 5 | The pytest framework, unit tests, debugging. |
| M6 | Level 6 | Kernel loading, the boot protocol, guest memory layout. |
| M7 | Level 7 | The virtio device model: virtqueues, MMIO, block/net. |
| M8 | Level 8 | A real issue reproduced, root-caused, fixed, reviewed. |
| M9 | Level 9 | Security model, snapshot compat, performance reasoning. |
M1 — Build, Test & Boot (the Floor)
Aligns to: Level 1. Certifies that you can build Firecracker with
tools/devtool, run its test suites, boot a microVM from your own binary, and that you understand
KVM well enough to have written a tiny VMM by hand.
Skills certified:
- Build (
tools/devtool build [--release] [-l musl|gnu]) and locate the binary underbuild/cargo_target/. - Run unit tests (
cargo test) and the pytest integration suite (tools/devtool test); passcheckstyle/checkbuild. - Boot a microVM via the
curl --unix-socketPUT sequence and read its state withGET /. - Open
/dev/kvm, create a VM and vCPU, map guest memory, runKVM_RUN, and handle an I/O exit — from scratch.
Self-check (no notes):
- Where does
tools/devtool build --releaseput thefirecrackerbinary, and whatfindcommand locates it without you guessing the path? What is the default C library? - Why is the integration suite run with
tools/devtool test(pytest) and notcargo test? What does each cover? - Name the three KVM file-descriptor levels and the ioctl that creates each.
- In your hand-written VMM, after
KVM_RUNreturns, what field do you read to decide what happened, and what does aKVM_EXIT_IOexit mean? - Walk through the minimal
curlsequence to boot a microVM — which call loads the kernel, which supplies the rootfs, which starts it? - What are the default
vcpu_countandmem_size_mibif you never callPUT /machine-config?
20-point rubric:
| Dimension | 0–2 (not yet) | 3 (adequate) | 4–5 (strong) |
|---|---|---|---|
| Build & test fluency | Build fails or needs help | Builds and runs tests; finds the binary | Builds release+debug, runs unit+pytest, checkstyle green, all from memory |
| Boot a microVM | Cannot reliably boot | Boots with the reference commands | Boots, reads state via GET /, explains each PUT and the defaults |
| KVM from scratch | VMM incomplete | VMM maps memory and runs KVM_RUN | VMM handles an IO/MMIO exit and you can explain every ioctl used |
| Anti-staleness habit | Relies on remembered paths | Uses rg/find when prompted | Reflexively locates code with rg/find; verifies version-sensitive facts |
M2 — The Contributor Workflow (Your First PR)
Aligns to: Level 2. Certifies that you can navigate the codebase and produce a clean, mergeable pull request that respects Firecracker's process.
Skills certified:
- Fork-and-pull flow against
main; branch hygiene; one logical change per commit. - DCO sign-off on every commit (
git commit -s;--amend -s;git rebase --signoff). - A
CHANGELOG.mdentry and a ≤72-char commit title; new functionality carries an integration test. - Reading a PR critically and reviewing someone else's.
Self-check (no notes):
- What exactly does
git commit -sadd, and why does the email have to match your GitHub account? - How many maintainer approvals does a PR need to merge, and who merges it?
- What three things does
CONTRIBUTING.mdexpect of each commit (besides the sign-off)? - Where do security vulnerabilities get reported, and why never as a public issue?
- Which
tools/devtoolcommands do you run before pushing, and what does clippy-as-errors mean for you? - You forgot
-son three commits already pushed to your branch. What is the exact recovery?
20-point rubric:
| Dimension | 0–2 | 3 | 4–5 |
|---|---|---|---|
| PR mechanics | Missing sign-off / CHANGELOG / failing CI | Signed, CHANGELOG present, CI green | Clean history, atomic commits, ≤72-char titles, CI green first try |
| Codebase navigation | Gets lost | Finds the right crate/module with help | Navigates vmm/firecracker/jailer fluently with rg/editor |
| Process understanding | Unsure of the rules | States DCO, ≥2 approvals, CHANGELOG, test rule | Explains why each rule exists (security, bisectability, trust) |
| Review skill | Cannot critique a PR | Spots obvious issues | Gives a substantive, kind, code-grounded review of a real PR |
M3 — Architecture & the Threading Model
Aligns to: Level 3. Certifies that you can trace a control-plane request from the API socket to the VMM thread and explain the three-thread architecture.
Skills certified:
- The three thread classes: API thread, VMM thread (EventManager epoll loop), one thread per vCPU.
- The API→VMM path:
ParsedRequest→VmmActionover anmpscchannel + an eventfd wake →PrebootApiController/RuntimeApiController→VmmData/VmmActionErrorreply. - Where this wiring lives (
rpc_interface.rs,api_server_adapter::run_with_api()), and that the API server is in thefirecrackerbinary, notvmm.
Self-check (no notes):
- Name the three thread classes and the single responsibility of each. Which is absent under
--no-api? - Trace
PUT /machine-configfrom the socket to where the config lands. What channel and what synchronization primitive carry the action across threads? - What is the difference between
PrebootApiControllerandRuntimeApiController, and what event switches between them? - The API server lives in which crate/binary —
vmmorfirecracker? Why does that boundary exist? - What does the VMM thread's EventManager loop actually wait on, and what wakes it?
- How does a vCPU thread communicate with the VMM thread (e.g. for pause/resume)?
20-point rubric:
| Dimension | 0–2 | 3 | 4–5 |
|---|---|---|---|
| Thread model | Confuses the threads | Names all three and their jobs | Explains the epoll loop, eventfd wakes, and the fast-path split precisely |
| Action channel trace | Cannot trace a request | Traces socket → VmmAction → handler | Traces both pre-boot and runtime paths, naming each type and the reply |
| Crate boundaries | Unsure where code lives | Knows API server is in firecracker | Explains the firecracker/vmm split and locates the channel wiring with rg |
| Diagramming | No coherent picture | Sketches the path | Draws the full three-thread + channel diagram from memory |
M4 — KVM, vCPUs & the Run Loop
Aligns to: Level 4. Certifies that you can read Firecracker's vCPU run loop without a guide and reason about VM exits and CPUID/MSR setup.
Skills certified:
- The
KVM_RUNloop and thestruct kvm_runshared page; locating it withrg 'fn run|KVM_RUN|VcpuExit' src/vmm/src/vstate/vcpu/. - The VM-exit taxonomy:
KVM_EXIT_IO(PIO) vsKVM_EXIT_MMIOvsHLT/SHUTDOWN/FAIL_ENTRY, and how Firecracker dispatches each. - The rust-vmm
kvm-ioctlsinterface (Kvm/VmFd/VcpuFd/VcpuExit) and CPUID/MSR setup (KVM_GET_SUPPORTED_CPUID/KVM_SET_CPUID2, CPU templates). KVM_IRQFD(eventfd→IRQ injection) andKVM_IOEVENTFD(guest write→eventfd; the virtio fast path).
Self-check (no notes):
- What does a single iteration of the run loop do, from
KVM_RUNto the nextKVM_RUN? - Distinguish
KVM_EXIT_IOfromKVM_EXIT_MMIO: which devices cause each, and where is the data? - What is the role of
KVM_IOEVENTFD, and why does it mean a virtio kick need not exit the vCPU thread? - Why does Firecracker call
KVM_SET_CPUID2, and what problem do CPU templates solve across heterogeneous hosts? - What happens on
KVM_EXIT_FAIL_ENTRY, and what kind of bug usually causes it? - How is an interrupt delivered to the guest — what ioctl, and what host primitive triggers it?
20-point rubric:
| Dimension | 0–2 | 3 | 4–5 |
|---|---|---|---|
| Run loop comprehension | Cannot follow it | Reads it with help | Reads vstate/vcpu/ unaided; explains each exit branch |
| VM-exit taxonomy | Confuses IO/MMIO | Knows the main exits | Maps every common exit to a device/cause and the data location |
| CPUID/MSR & templates | Unaware | Knows CPUID is set | Explains normalization across hosts and the template mechanism |
| eventfd plumbing | Unaware | Knows IRQFD/IOEVENTFD exist | Explains the fast path: kick→IOEVENTFD→VMM loop, IRQ via IRQFD |
M5 — Testing & Debugging
Aligns to: Level 5. Certifies that you can use Firecracker's test machinery to prove a change and to chase a bug, including flaky-test diagnosis.
Skills certified:
- The pytest integration framework in
tests/, run viatools/devtool test [-- <pytest args>]. - Writing a unit test (
cargo test) and a Rust-side assertion; not lowering coverage. - Writing an integration test that boots a microVM and asserts behavior.
- Diagnosing a flaky test (ordering, timing, resource leakage) and debugging a running microVM.
Self-check (no notes):
- Where do integration tests live, what drives them, and how do you run a single one?
- When does a change require an integration test versus a unit test? (Recall the CONTRIBUTING rule.)
- Name two common causes of flaky integration tests in a VMM test harness and how you'd confirm each.
- How do you observe what a microVM is doing at runtime — what signals, logs, or metrics are available?
- What does
tools/devtool checkbuild --allprotect against that a singlecargo builddoes not? - You have a test that passes locally but fails in CI. What is your first diagnostic move?
20-point rubric:
| Dimension | 0–2 | 3 | 4–5 |
|---|---|---|---|
| Test authoring | Cannot add a test | Writes a unit or integration test | Writes both; chooses the right kind; runs targeted via tools/devtool test |
| Debugging a microVM | No method | Reads logs | Uses logs/metrics/serial + a debugger to localize a fault |
| Flaky-test diagnosis | Reruns and hopes | Identifies a likely cause | Reproduces deterministically and explains the race/leak |
| CI/coverage discipline | Ignores CI | Keeps CI green | Reasons about coverage, checkbuild --all, and CI/local divergence |
M6 — The Boot Process & Guest Memory
Aligns to: Level 6. Certifies that you can trace how a kernel is loaded and started, and reason about the guest-physical memory layout.
Skills certified:
- Kernel loading: uncompressed
vmlinuxELF vialinux-loader(PT_LOAD→ copy →e_entry). - The x86_64 boot protocol:
boot_params/zero page, the e820 map, cmdline and initrd pointers; layout constants inarch/x86_64/layout.rs(verify on your branch). - Initial vCPU register state for 64-bit long mode (
rip=e_entry,rsi=ZERO_PAGE_START, paging on). - aarch64 differences: no zero page, an FDT/DTB passed in
x0, the arm64Image. - Guest memory:
GuestMemoryMmap/GuestAddress, the MMIO gap below 4 GiB, high RAM above.
Self-check (no notes):
- Why is there no BIOS, and what two jobs (firmware's and the bootloader's) does the VMM do instead?
- What is the zero page, and name three things it carries to the kernel.
- Why does the e820 map matter — what breaks if it is wrong?
- What initial register values put the guest into 64-bit long mode at the kernel entry?
- How does aarch64 boot differ from x86_64 at the "hand the kernel its environment" step?
- Why is there an MMIO gap below 4 GiB, and where does RAM above it go?
20-point rubric:
| Dimension | 0–2 | 3 | 4–5 |
|---|---|---|---|
| Kernel load path | Cannot trace it | Traces ELF load with help | Traces linux-loader PT_LOAD → entry → first instruction unaided |
| Boot protocol | Vague on zero page | Knows zero page + e820 | Explains boot_params fields, cmdline, initrd, and failure modes |
| Initial CPU state | Unsure | Knows long mode is set up | States the exact register setup and why each is needed |
| Memory layout | Confuses address spaces | Knows guest-phys vs host | Explains the full layout, MMIO gap, high RAM, and aarch64 FDT |
M7 — The Virtio Device Model
Aligns to: Level 7. Certifies that you can trace a virtio I/O end to end and reason about the virtqueue/MMIO machinery well enough to extend it.
Skills certified:
- Split virtqueues: descriptor table (
addr/len/flags/next, flags NEXT/WRITE/INDIRECT), available ring, used ring; the kick/interrupt cycle. - The virtio-MMIO transport: the register map (
MagicValue,QueueSel,QueueNotify,InterruptStatus,Status), feature negotiation, the status state machine (ACKNOWLEDGE→DRIVER→FEATURES_OK→DRIVER_OK). - virtio-block (one request queue, host
pread/pwrite, I/O engine) and virtio-net (RX/TX, host TAP); where they live (src/vmm/src/devices/virtio/{block,net}/). - How a device is placed on the bus (
MMIODeviceManager) and how the fast path usesKVM_IOEVENTFD.
Self-check (no notes):
- Trace a guest disk read from the driver placing a descriptor to the guest seeing the result — every ring, the kick, the host call, and the interrupt.
- What do the descriptor flags NEXT, WRITE, and INDIRECT each mean?
- What is the status handshake, and what goes wrong if a device claims a feature the driver did not acknowledge?
- How does the guest learn where a virtio-MMIO device's registers and IRQ are on x86 vs aarch64?
- Why is the "kick" not necessarily a vCPU exit, and how is the completion interrupt delivered?
- Where is virtio-block's request handling, and what host syscall actually reads the data? Find it
with
rg.
20-point rubric:
| Dimension | 0–2 | 3 | 4–5 |
|---|---|---|---|
| Virtqueue mechanics | Confused by the rings | Knows avail/used/desc roles | Traces a descriptor chain and the kick/interrupt cycle precisely |
| MMIO transport | Unaware of the register map | Knows key registers | Explains negotiation, the status FSM, and device discovery on both arches |
| A specific device | Cannot locate it | Finds block/net with rg | Traces block or net I/O to the host syscall and back to the used ring |
| Extensibility | No idea how to add a device | Knows the device trait shape | Could scaffold a new virtio device on the MMIO bus |
M8 — Real Issue Contribution
Aligns to: Level 8. Certifies that you can take a real, open issue from reproduction through root cause to a reviewed PR.
Skills certified:
- Selecting an appropriately scoped real issue and posting a minimal, reliable reproduction.
- Execution-path analysis: tracing the bug through the actual code, not guessing.
- A correct fix with an integration/unit test that fails before and passes after.
- A PR description tying fix to root cause; responding to ≥2 maintainers across review rounds.
Self-check (no notes):
- What makes a reproduction "good" enough that a maintainer can confirm it?
- How do you distinguish a symptom from a root cause, and how do you prove the root cause?
- Why must your test fail before your fix and pass after, and where does it go?
- What belongs in a PR description so a reviewer can evaluate it quickly?
- A maintainer pushes back on your approach citing the minimal-device-model philosophy. How do you respond?
- How do you keep your branch clean across multiple review rounds (rebase, re-sign, squash)?
20-point rubric:
| Dimension | 0–2 | 3 | 4–5 |
|---|---|---|---|
| Reproduction | Vague/flaky | Reproduces reliably | Minimal, deterministic repro that maintainers confirm |
| Root-cause analysis | Guesses | Finds the area | Proves the root cause via execution-path analysis |
| Fix & test | No test / wrong fix | Fix + a test | Minimal correct fix; test fails-before/passes-after; no coverage loss |
| Review collaboration | Defensive/silent | Responds to feedback | Engages every comment, iterates cleanly, helps review others |
M9 — Advanced Maintainer
Aligns to: Level 9. Certifies maintainer-grade reasoning about security, snapshot compatibility, and performance.
Skills certified:
- The defense-in-depth model: KVM boundary + jailer (chroot/namespaces/cgroups/priv-drop) +
seccomp-BPF (per-category
vmm/api/vcpufilters) + Rust; the explicit threat model (guest+guest-kernel untrusted). - Snapshot/restore internals and backward compatibility: the
Persisttrait, full vs diff,track_dirty_pages, UFFD; why a snapshot taken on version N must load on N+1 within policy. - Performance reasoning: boot time, oversubscription/density, I/O engines, hugepages; diagnosing a regression with measurement and bisection.
Self-check (no notes):
- Name the four defense-in-depth layers and what each stops. Which one does the jailer not do (hint: seccomp)?
- How are seccomp filters structured (per-category, default action + rules), and what does
--no-seccompmean for production? - What is the threat model in one sentence, and why is the device model deliberately minimal?
- What makes a change a snapshot-compatibility break, and how would you reason about whether it is acceptable?
- How does diff snapshotting work, and what must be enabled for it?
- You see a boot-time regression after a change. What is your measurement-and-bisection plan?
20-point rubric:
| Dimension | 0–2 | 3 | 4–5 |
|---|---|---|---|
| Security model | Vague | Names the layers | Explains each layer, audits a device's surface, reasons about the threat model |
| Snapshot compatibility | Unaware of the risk | Knows full vs diff | Reasons correctly about cross-version compat and the Persist contract |
| Performance reasoning | Anecdotal | Measures something | Diagnoses a regression with rigorous measurement + bisection |
| Maintainer judgment | Defers entirely | Has opinions | Weighs surface area, compat, and perf like a maintainer would |
Passing the Whole Set: Capstone Readiness
You are ready for the Capstone when all nine milestones are passed (≥16/20 each, no dimension below 3) — and especially when M4, M7, and M8 are strong, because the capstone leans hardest on reading the run loop, tracing a device, and shepherding a real PR. The capstone is scored against its own evaluation rubric, which is these milestones applied to one continuous, real contribution.
If you are short on any milestone, the fix is always the same: re-do the weakest labs and read the deep dives they reference, then re-test. Do not paper over a gate — a contributor who skipped M4 will stall in the capstone the moment a bug lives in the vCPU run loop.
Where to Go Next
If you have not started yet, begin with Level 1 and return here after each level to test the matching milestone. Keep the 16-Week Plan open alongside this page — the plan tells you when to test; this page tells you whether you passed. The internals behind every self-check live in the Deep Dives.