Firecracker Architecture: How It Actually Works
How to read this page
Symvanta parsed this repository into a code graph: every function, class, and method is a node, and every call or import between them is an edge. Everything on this page is computed from that graph at the commit shown above. The terms:
- Module (or cluster)
- A group of symbols that call each other far more than they call anything else. An algorithm called Louvain community detection finds these groups from the call traffic alone; nobody draws them by hand.
- Modularity (the Q number)
- A 0-to-1 score of how cleanly those groups separate. Higher means more call traffic stays inside its own group; scores around 0.7 and above read as clean boundaries.
- Hub
- The most depended-upon symbol inside one module.
- Load-bearing symbols
- PageRank, the algorithm Google originally used to rank web pages, run over the call graph instead: it surfaces the functions the rest of the codebase leans on hardest.
- Arrows and their numbers
- How many calls cross from one module into another. A heavier arrow means tighter coupling between those two parts.
- Dependency cycle
- File A imports B, which imports A again, sometimes through a longer loop. Cycles are not bugs, but a change inside one tends to ripple around the whole loop.
- Mutually recursive symbols
- Functions that call each other, usually the natural shape of parsers and tree-walking code.
Firecracker is the virtual machine monitor behind AWS Lambda and Fargate: one
Rust binary that boots stripped-down microVMs on KVM, driven by a REST API
over a unix socket, with its own virtio devices, seccomp filters, and a
jailer. Symvanta's graph at 81b38b9 detects 46 functional modules
(modularity Q=0.79), and the three biggest clusters are guest memory and the
device trait built on it (886 symbols, hub GuestMemoryMmap), the virtio
descriptor ring (739, hub Queue), and the VMM core the API drives (714, hub
Net).
The map is clean for a systems repo. The hubs are real domain types
(GuestMemoryMmap, Queue, ParsedRequest, VirtioBlock), the graph
carries zero dependency cycles and just 2 pairs of mutually recursive
symbols, and the heaviest edge runs from the VMM core into guest memory (210
calls). The devices sit in a ring around guest memory, which is what a VMM
looks like when every device's job is moving bytes in and out of the guest.
Two virtio transports coexist in that ring: the MMIO transport in
devices/virtio/transport/mmio.rs, and a PCI stack of 460 symbols under
vmm/src/pci whose VirtioPciDevice puts the same devices on a PCI bus.
Module map
The diagram shows the 10 largest of the 46 detected modules, with edges
weighted by how many calls cross between them. The biggest holds 886 symbols
and its hub is GuestMemoryMmap, the guest-physical-memory handle every
virtio device keeps a clone of. Module names are checked by hand against the
files their members live in and renamed where the generated label misread the
cluster; the symbol counts, hubs, and edge weights are what the graph
computed.
Where to start reading
The raw PageRank ranking says little on this repo. Half of its top twelve are
scaffolding: http_request is a helper inside the parsed-request test module
(api_server/parsed_request.rs:423), single_region_mem comes from
vmm/src/test_utils, default_virtio_mem, default_mem and default_vmm
are per-suite defaults, and is_activated is a DummyDevice stub in the MMIO
device manager. The other half are converters and bare method names:
u64_to_usize, write_be_u16, host_page_size, plus new, add and
setup. A hypervisor's most-called functions are tiny helpers. The list below
is built from the boot path and the hub of each cluster in the diagram.
build_microvm_for_bootParsedRequestVmmActionGuestMemoryMmapVirtioDeviceQueueDeviceStateNetVirtioBlockVirtioPciDevice
Key subsystems
Guest memory and the device trait
GuestMemoryMmap is a collection of GuestRegionMmapExt regions
(vstate/memory.rs:38 and :399), and the graph clusters it with the
VirtioDevice trait every device implements and the KvmVm handle they are
wired against. 886 symbols, the largest module in the repo, and its heaviest
outbound edge is the 136 calls into the VMM core.
Virtio queues and rate limiting
The descriptor ring in devices/virtio/queue.rs: available and used ring
parsing, descriptor chain walking, and the RateLimiter token buckets that
throttle block and net traffic. 739 symbols. The cluster also holds the
VirtQueue fixture from devices/virtio/test_utils.rs that the device suites
build rings with.
VMM core and machine configuration
The Louvain label for this cluster is the Net device, its hub, but the
membership is the VMM top level: VmmError and FcExitCode in
vmm/src/lib.rs, the EventManager loop, and the vmm_config structs
(BootSourceConfig, RateLimiterConfig) that describe a machine before it
boots. 714 symbols, and it carries the heaviest edge on the map: 210 calls
into guest memory.
API server and VmmAction
The control plane. ParsedRequest
(firecracker/src/api_server/parsed_request.rs:62) turns each request on the
API socket into a VmmAction from rpc_interface.rs, and VmResources
accumulates the machine config before boot. 646 symbols, and 196 of its calls
land in the VMM core.
Device activation and vsock
DeviceState (devices/virtio/device.rs:35) is the enum every virtio device
carries to say whether it is inactive or active, and the graph clusters it
with the virtio-vsock packets, connections, and unix-socket muxer under
devices/virtio/vsock. 642 symbols.
Dumbo MMDS network stack
InnerBytes, MacAddr, and Connection from vmm/src/dumbo: a hand-written
TCP stack that serves the metadata service (MMDS) to guests directly from the
VMM process, with the Mmds data store behind it. 555 symbols, and only 26
calls leave it, 14 of them into the API server that exposes MMDS.
PCI and the virtio-pci transport
Configuration space and BAR programming in vmm/src/pci, MSI-X interrupt
tables in pci/msix.rs, and the VirtioPciDevice transport that puts a
virtio device on the PCI bus. 460 symbols, hub PciSBDF, the segment, bus,
device and function address a config access is decoded from.
io_uring engine and bindings
The async block-IO engine: the submission and completion rings in
vmm/src/io_uring, IoUringError, and the bindgen kernel ABI types
(io_uring_sqe, Cqe, the __u32 family) the ring shares with Linux. 459
symbols, and nearly a leaf: 8 outbound calls, 5 of them into the block device
that drives it.
Block devices
The virtio-block device in devices/virtio/block/virtio, its vhost-user
variant, and the cache-type and file-engine selection a block device boots
with. 459 symbols, and its heaviest edge is the 133 calls into the queue
cluster, which is what a block device does for a living.
Shared infrastructure
The tenth cluster in the diagram is CPU templates and CPUID (396 symbols, hub
CustomCpuTemplate): the register and CPUID leaf modifiers under
vmm/src/cpu_config. Just outside it sit metrics and logging (356, hub
SharedIncMetric), the jailer binary (333, hub JailerError), and machine
configuration around the vm-memory crate's GuestRegionMmap (282), the one
hub in the top fifteen that is not defined in this repo at all.
Canonical request flow
The sequence worth reading first is the microVM build: everything between an
InstanceStart action and vcpu threads running. Allocate guest memory, open
KVM, create the VM and its vcpus, register the memory regions, build the
device manager, load the kernel, attach the block and net devices, configure
the system for boot, then start the vcpus under their seccomp filters. Every
step below is a call out of build_microvm_for_boot, in the order the work
happens. Three of them are compiled per architecture: the links point at the
x86_64 copy, and aarch64 carries a twin of each.
build_microvm_for_boot(builder.rs:143) takes the boot source, the accumulatedVmResources, the event manager, and the seccomp filter map, and returns a pausedVmm.VmResources.allocate_guest_memory(resources.rs:519) turns the machine config's memory size and huge-page setting into a vector of guest regions.Kvm.new(vstate/kvm.rs:29) opens/dev/kvmand applies the capability modifiers the CPU template asked for.KvmVm.new(arch/x86_64/vm.rs:74) creates the VM file descriptor and the arch-specific interrupt state. The struct is per-arch; its shared methods come from oneimpl KvmVmblock invstate/vm.rs:141.KvmVm.create_vcpus(vstate/vm.rs:200) creates oneVcpuper configured vcpu, each with its own exit event fd.KvmVm.register_dram_memory_regions(vstate/vm.rs:453) hands the allocated regions to KVM as guest memory slots.DeviceManager.new(device_manager/mod.rs:233) builds the MMIO bus, the PCI bus when PCI is enabled, the serial console, and the resource allocator every later attach draws addresses from.load_kernel(arch/x86_64/mod.rs:500) reads the kernel image into guest memory and returns its entry point, thenInitrdConfig::from_configplaces the initrd behind it.attach_block_devices(builder.rs:659) registers each configured block device on the bus and appends its root-device arguments to the kernel command line.attach_net_devices(builder.rs:691) does the same for the net devices, andattach_pmem_devicesfollows for persistent memory.configure_system_for_boot(arch/x86_64/mod.rs:237) applies the CPU template to every vcpu, writes the boot parameters and the device information the guest reads at startup, and seals the command line.KvmVm.start_vcpus(vstate/vm.rs:243) moves each vcpu onto its own thread, installs thevcpuseccomp filter there, and leaves the state machine paused until the API says resume.
Health signals
Symvanta detected 0 dependency cycles across 46 modules (modularity Q=0.79). 2 sets of mutually recursive symbols were also detected, the largest being transport (2 symbols). Both pairs are small and local: MmioTransport.set_device_status (transport/mmio.rs:165) loops back through the transport's own write, and the second pair sits in the virtio queue fixtures.
Auto-generated by Symvanta from the public repo firecracker-microvm/firecracker at commit 81b38b9 , licensed Apache-2.0 .
Machine-readable companion: data.json (module counts, subsystems, load-bearing symbols, health signals).