Chapter 16: Snapshot And Restore
A Firecracker snapshot captures a paused machine, not merely a boot shortcut.
The saved state includes guest memory, vCPU registers, in-kernel KVM state, and
the emulated device state needed to continue execution. Restore rebuilds those
objects in a new firecracker process and starts its vCPU threads in Paused
state. The caller decides when they run.
That continuation property is both the value and the danger. A clone resumes with the kernel, application heaps, random-number generators, clocks, tokens, and protocol state present at the snapshot point. Restore avoids repeating initialization; it does not make duplicated state unique or current.
Creating A Snapshot
Snapshot creation is a runtime operation, but the microVM must already be paused:
PATCH /vm
{"state":"Paused"}
PUT /snapshot/create
{"snapshot_type":"Full","snapshot_path":"state.snap","mem_file_path":"memory.snap"}
These requests open KVM and snapshot files and therefore belong on an isolated
Linux host with KVM access, or in a VM with nested virtualization. A successful
create writes two artifacts. snapshot_path receives serialized VMM and KVM
state. mem_file_path receives guest memory. Attached block images are
external resources and are not copied into either file.
Pausing stops vCPU execution while the event loop remains alive. During device
save, Firecracker calls each device's prepare_save. File-backed block devices
drain outstanding operations and synchronize the backing file to the host
filesystem. That does not package the file with the snapshot or guarantee that
the host filesystem has committed it to durable media. The operator must keep
the matching block images and coordinate any stronger durability requirement.
Snapshot creation also resets an active vsock transport. When the original VM or a restored VM resumes, the guest driver closes existing connections while listen sockets remain. Creating a snapshot is therefore not observationally invisible even though the guest CPUs are paused.
The state file is written and synchronized before the memory dump. A failure while writing memory can consequently leave an unusable partial artifact set; callers should publish the pair only after the API reports success.
The State File
The current writer serializes a header and MicrovmState with bitcode, then
appends an eight-byte CRC-64:
The architecture-specific magic rejects an x86_64 state file on aarch64 and the reverse. The format version is independent of the Firecracker release version. The current loader requires the same major version and accepts a snapshot minor version no newer than its own; the patch component does not participate in that check. A 10,000,000-byte deserialization limit bounds the state file, not the guest-memory file.
The CRC detects accidental corruption. It is not authentication and says nothing about the block images or memory file. Snapshot artifacts can contain secrets and executable guest state, so an integrator must protect their confidentiality, integrity, ownership, and provenance outside Firecracker.
MicrovmState has five top-level parts:
VmInfo carries such configuration as memory size, SMT, boot-source paths,
the static CPU-template choice, and huge-page policy. KvmState, VmState,
and the per-vCPU vector preserve KVM capability modifiers, VM state, and CPU
state. DevicesState now covers MMIO and optional PCI transports, ACPI
VMGenID and VMClock, serial emulation, and the supported virtio devices.
Vhost-user block is explicitly unsnapshottable. Vsock configuration and
frontend state are saved, but live host connections are not continued.
Some process configuration is deliberately outside the state. Log and metrics destinations must be configured in the new process. MMDS configuration is saved with network-device state, but the MMDS data store is not. Host resources remain references: block paths must resolve, TAP devices must exist, and the vsock backend socket must be reachable when the new process restores devices.
Full And Differential Memory
snapshot_type defaults to Full. A full snapshot writes every plugged memory
slot and seeks across unplugged virtio-mem slots, producing a file whose logical
length describes the complete memory layout. The holes are sparse ranges; they
are not ballooned pages. Creating the full image faults guest pages into the
Firecracker process and can be much more expensive than loading that image
later on demand.
Diff is a developer-preview format. It writes a full state file but only a
layer of guest-memory pages considered dirty since boot or the preceding
snapshot. A general diff layer is not a standalone memory image. Merge layers
over the base memory file in creation order, then pair the resulting memory
image with the state file produced alongside the last merged layer.
Firecracker has two ways to select pages for a diff. With
track_dirty_pages: true, KVM_GET_DIRTY_LOG reports vCPU writes and the
userspace AtomicBitmap covers writes performed through Firecracker's guest
memory mappings. Firecracker conservatively marks activated virtqueue memory
dirty after each snapshot because queue-object accesses are not marked during
normal device operation.
Without KVM dirty tracking, Firecracker uses mincore(2) residency as an
over-approximation. This works only with swap disabled: a dirty page moved to
swap is not resident and can be omitted incorrectly. The fallback may also
include clean resident pages. Dirty tracking is more precise but adds runtime
cost and negates much of the benefit of huge pages.
flowchart LR
A["Paused VM"] --> B{"Dirty tracking?"}
B -->|"yes"| C["KVM dirty log"]
B -->|"yes"| D["Userspace memory bitmap"]
C --> E["Union selected pages"]
D --> E
B -->|"no"| F["mincore resident pages"]
E --> G["Sparse diff layer"]
F --> G
G --> H["Merge over base in creation order"]
H --> I["Resumable memory image"]
When a diff target already exists with the expected logical size, Firecracker writes selected pages into that file in place. This can merge directly into a base, but it also means the caller must not mistake a same-sized unrelated file for a disposable output. After a successful snapshot, Firecracker clears the dirty state used for the next interval and pre-marks virtqueue pages again.
Saving vCPU State
KVM does not expose a single save-vCPU ioctl. On x86_64, Firecracker reads
KVM_GET_MP_STATE first because accepting pending APIC events can modify later
state. It then reads general and special registers, XSAVE and XCR state, debug
registers, LAPIC state, TSC frequency, CPUID, and selected MSRs.
KVM_GET_VCPU_EVENTS comes last because earlier reads can affect pending
exceptions, interrupts, NMIs, and SMIs.
Restore has its own dependency order. KVM_SET_CPUID2 precedes MP state.
General registers precede vCPU events because KVM_SET_REGS clears pending
exceptions. Special registers precede LAPIC state because they restore the
APIC-base MSR, and LAPIC state precedes the saved MSRs because the TSC-deadline
MSR depends on LAPIC timer mode. KVM_SET_VCPU_EVENTS is last. All other vCPUs
must remain stopped while these event structures are restored.
Device state is saved before KVM state. An async block device can complete work
and inject an interrupt while prepare_save drains it; saving KVM first would
lose that interrupt from the restored machine.
Loading A Snapshot
PUT /snapshot/load is a pre-boot operation. Apart from logs and metrics, the
new process must not already have a machine configuration. The request names
the state file and a memory backend:
PUT /snapshot/load
{
"snapshot_path": "state.snap",
"mem_backend": {
"backend_path": "memory.snap",
"backend_type": "File"
},
"track_dirty_pages": false,
"resume_vm": false
}
The older top-level mem_file_path spelling selects a file backend but is
deprecated. track_dirty_pages is not stored in the snapshot; a caller that
wants precise later diff snapshots must request it again during load.
Before building KVM objects, Firecracker validates the state file, applies
network_overrides to TAP names and an optional vsock_override to the host
socket, checks the memory-region description, and warns if the saved and host
CPU vendor or manufacturer differs. There is no corresponding block-path
override in the load request.
Restore then constructs KVM and vCPU fds, registers the restored memory
regions, restores each vCPU, restores VM state, and reconstructs devices. ACPI
device restore changes VMGenID and VMClock state only after KVM interrupt state
has been restored, so their notifications are not overwritten. The vCPU host
threads start paused. resume_vm: true resumes them before the load request
completes; otherwise the caller later sends PATCH /vm with Resumed.
sequenceDiagram
participant A as API caller
participant F as Firecracker
participant M as Memory backend
participant K as KVM
A->>F: PUT /snapshot/load
F->>F: Validate state and apply overrides
F->>M: Map file or register UFFD regions
F->>K: Create VM and vCPUs
F->>K: Register memory regions
F->>K: Restore vCPU and VM state
F->>F: Restore devices and start paused threads
opt resume_vm is true
F->>K: Resume vCPUs into KVM_RUN
end
F-->>A: 204 No Content
File-Backed Memory
The File backend maps each region from the memory image with MAP_PRIVATE.
Reads fault pages through the host page cache. The first guest write creates a
private anonymous copy, leaving the file unchanged. Multiple restored processes
can therefore share clean page-cache pages while diverging only where each
guest writes.
This is demand paging, not an eager copy. Startup can be quick because restore does not read all guest memory first; later page faults move part of the cost onto the guest's execution path. Working-set shape, storage latency, page-cache state, vCPU count, memory size, and devices all affect observed resume latency.
The mapped memory image must remain immutable for the life of the restored process. External modification can change data supplied by later faults and corrupt the guest. The file backend cannot restore hugetlbfs-backed snapshots; Firecracker requires UFFD for those.
Userfaultfd Memory
The Uffd backend gives fault servicing to an external process. Firecracker
creates anonymous guest mappings, creates a userfaultfd, registers each region,
and connects to the Unix socket named by backend_path. It sends the fd with
SCM_RIGHTS and a JSON array of region mappings containing host address, size,
backing offset, and page size. No continuing control protocol runs over that
socket.
The handler reads UFFD_EVENT_PAGEFAULT and supplies data with operations such
as UFFDIO_COPY. Because page faults can occur while Firecracker itself is
restoring state, the handler must be listening and ready before the load
request. Firecracker keeps its own UFFD fd open. If the handler disappears,
future faults wait with no service and the guest can freeze indefinitely;
there is no automatic fallback to the snapshot file.
Balloon discard produces UFFD_EVENT_REMOVE. A correct handler records those
ranges as zero so that a later fault does not resurrect pre-discard data from
the snapshot. The handler is part of the VM's availability and isolation
boundary and must be monitored and confined accordingly.
Clone Uniqueness
Restoring one snapshot more than once duplicates everything already consumed
or cached at the snapshot point. That includes kernel and userspace PRNG state,
UUID sequences, /proc/sys/kernel/random/boot_id, credentials, TLS material,
session tokens, database connection state, and application counters. Remapping
a TAP device does not repair any of them.
Firecracker always exposes VMGenID. On restore it generates a new 16-byte ID, writes it into guest memory, and notifies the guest. Linux ACPI support arrived in 5.18; DeviceTree support arrived later and must be backported to the Linux 6.1 aarch64 kernels supported by Firecracker. A supported kernel mixes the new ID into its CSPRNG and reseeds. There is still a window before the guest handles the notification, and VMGenID cannot reset entropy cached inside an application library.
VMClock provides a separate userspace-visible generation counter. Firecracker increments it atomically before vCPUs resume. Guest software with the relevant VMClock kernel support can map or poll the device and rebuild userspace state when the counter changes. Upstream support is newer than Firecracker's current guest-kernel baseline, so deployments need an appropriate backport or newer kernel. Neither device can identify every duplicated token automatically; the application must define its resume boundary and reacquire per-instance state before serving work.
The safest snapshot point is after generic runtime initialization but before the guest obtains request-specific secrets or performs externally visible work. If the original VM and a clone both continue from the same state, a value meant to be used once can be used twice even if only one clone was created.
Clocks And Connections
By default, x86 kvmclock resumes from the saved value. Time spent while the
snapshot was dormant is not added. clock_realtime: true asks KVM to advance
kvmclock by elapsed host real time through KVM_CLOCK_REALTIME; it is x86_64
only, requires host support, and can expose a sudden jump to the guest. Guest
wall-clock synchronization remains an operating-system responsibility.
Network connectivity is not guaranteed across resume. TAP overrides change the host endpoint, not IP addresses, routes, TCP sequence numbers, peer state, or credentials in guest memory. Vsock deliberately reports a transport reset and closes existing connections after resume. A clone protocol should treat external connections as invalid and reconnect explicitly.
Compatibility Boundaries
A matching snapshot format is necessary but not sufficient. Firecracker's documentation requires equivalent software and hardware configuration. Snapshots do not cross architectures, and Intel-to-AMD restore is unsupported even though current source only warns on a vendor mismatch before later restore steps fail or misbehave. CPU features exposed to the guest must remain invariant; a suitable CPU template can help only within its documented host set.
Host-kernel changes can alter the semantics of saved KVM state. Firecracker calls cross-kernel restore unstable and publishes only narrow tested cases. AArch64 snapshots also cannot cross GIC versions. Restore hosts must reproduce block paths and permissions and make required TAP and vsock endpoints available. cgroup v2 is strongly recommended because cgroup v1 is associated with high restore latency.
Treat state file, memory image, block images, CPU contract, host kernel, and external endpoints as one versioned artifact set. Valid CRC and compatible format numbers cannot make a mismatched set safe to resume.