Enable pre-baked VM snapshots as baseline for lab exercises. Trainees spawn fully-booted instances in 28ms instead of 3-5s, each with their own writable COW copy.
Use case: "Here's a working web server, now misconfigure it" or "This VM has tools you need, solve the problem"
- Cold boot a reference VM normally via Firecracker
- Wait for guest agent to signal "ready" (via
/api/readyor similar) - Firecracker API:
pausethe VM, thencreate_snapshot(memory + device state) - Store snapshot tarball:
snapshot.tar.gz(memory dump + metadata)
- Copy snapshot files to per-VM location using COW (nearly instant)
- Firecracker API:
load_snapshot+ start with snapshot flag - VM resumes from frozen state (28ms total)
- Agent reconnects and re-runs lightweight setup (hostname, network)
Create new file vm/snapshot.go:
// CaptureSnapshot(ctx, cfg, outputPath) error
// - Start VM normally via vm.New()
// - Wait for agent /ready endpoint (poll agent HTTP)
// - Call Firecracker pause() API
// - Call Firecracker create_snapshot(outputPath) API
// - Return path to snapshot tarball
// RestoreSnapshot(ctx, cfg, snapshotPath) (*Machine, error)
// - Validate snapshot exists
// - Copy snapshot files to per-VM location (COW)
// - Call Firecracker with load_snapshot flag
// - Return *MachineKey decisions:
- Snapshot format: Firecracker's native binary (memory dump + metadata)
- Storage: Host filesystem, path passed as CLI arg
- Metadata: Store snapshot creation time, rootfs hash, agent version in separate JSON file
onfire run --rootfs rootfs.ext4 --snapshot-out snapshot.tar --snapshot-capture
- Start a reference VM
- Wait for readiness
- Capture snapshot
- Exit
onfire fleet create --count 5 --snapshot snapshot.tar
- Per-VM: copy snapshot to
vm-{id}-snapshot - Per-VM: restore from snapshot + agent reconnect
- Parallel launch (same as today, but 28ms vs 3-5s per VM)
REST API:
- POST
/api/v1/fleetsacceptssnapshot_pathfield (mutually exclusive withrootfs) - Routes to
launchFleetFromSnapshot(ctx, snapshotPath, vmIDs, deps)
CLI client (onfirec/main.go):
onfirec fleet create --count 5 --snapshot /path/to/snapshot.tar
- Sends
"snapshot_path": "/path/to/snapshot.tar"in request body
- Mock Firecracker API; verify pause + create_snapshot called correctly
- Mock agent readiness check; simulate timeout
- Verify COW copy behavior (reflink fallback)
- Capture snapshot from a real VM (if CI has /dev/kvm)
- Restore snapshot; verify
/etc/hostnameset correctly - Verify agent can reconnect post-restore
| File | Change |
|---|---|
vm/snapshot.go |
NEW: CaptureSnapshot(), RestoreSnapshot() |
vm/snapshot_test.go |
NEW: Tests for snapshot capture and restore |
cmd/run.go |
Add --snapshot-out + --snapshot-capture flags; add capture logic |
cmd/fleet.go |
Add snapshot restore path (parallel to launchFleet) |
api/fleet.go |
Add SnapshotPath to fleetCreateRequest; route to restoreFleet() |
api/server.go |
Extend LaunchDeps interface with snapshot methods |
onfirec/main.go |
Add --snapshot flag to fleet create; update usage |
// Pause (to freeze state)
PUT /vm.pause
{}
// Snapshot (capture memory + device state)
PUT /snapshot/create
{
"snapshot_type": "Full",
"snapshot_path": "/path/to/snapshot/memory",
"mem_file_path": "/path/to/snapshot/memory-dump"
}
// Load snapshot (at restore time)
PUT /snapshot/load
{
"snapshot_path": "/path/to/snapshot/memory",
"mem_file_path": "/path/to/snapshot/memory-dump",
"enable_diff_snapshots": false
}-
Per-VM customization: After snapshot restore, hostname is stale
- Fix: Agent runs lightweight script to update
/etc/hostnamevia debugfs - Or: Pre-bake per-VM snapshots (defeats space savings; skip for now)
- Fix: Agent runs lightweight script to update
-
Snapshot size: ≈ MemMB per snapshot (e.g., 512MB VM → 512MB snapshot file)
- Acceptable for 5-10 concurrent trainees
- Discuss if scaling beyond that
-
Snapshot versioning: If rootfs changes, snapshots become invalid
- Metadata file with build hash + instructions to recreate snapshot
- CI/build system should regenerate on rootfs change
-
Agent readiness: Need robust way to detect when guest is ready for snapshot
- Agent exposes
/api/readyendpoint returning{ "ready": true } - Timeout after 30s → error
- Agent exposes
Phase 1 (foundation):
- Implement vm/snapshot.go (capture + restore logic)
- Add
onfire run --snapshot-capture - Write tests
Phase 2 (CLI):
- Integrate into fleet creation
- Update onfirec CLI
- Manual testing
Phase 3 (optional polish):
- Metadata versioning + auto-regeneration
- Diff snapshots (incremental) if memory usage is concern
- Concurrent snapshot capture (for multiple pre-baked VM types)
- ✓
onfire run --snapshot-captureproduces valid snapshot.tar - ✓
onfirec fleet create --count 5 --snapshot snapshot.tarlaunches 5 VMs in <500ms total - ✓ Each VM has correct hostname + working agent
- ✓ All existing tests pass
- ✓ New snapshot tests achieve >80% coverage