Cloud Hypervisor PoC — sub-second restore + fork-from-warm-base

Cloud Hypervisor PoC — sub-second restore + fork-from-warm-base

Spike: #644 — follow-up from #639 (memory snapshot spike) and joint with #642 (sub-second start). Date: 2026-07-17 · Host: `grissom` (Linux/KVM, in `kvm` group). Type: PoC / findings. Verdict: GO for the fast-start/fork path.

All numbers are real, measured against Cloud Hypervisor v53.0 (upstream static build) booting a

flattened copy of the actual agent base image (`ubuntu-server-24.04-agent.qcow2`). This is the

empirical counterpart to #639, whose QEMU/q35 baseline was 3.7–6.2 s restore.


TL;DR

  • CH boots the agent base in ~0.5 s and **restores a snapshot to a resumed VM in 43 ms (ondemand)

/ 134 ms (copy) — i.e. sub-second**, ~30–140× faster than the QEMU/q35 `virsh restore` path measured in #639 (3.7–6.2 s). This settles the core question #639 left open.

  • CH supports virtiofs (`--fs`) + vsock (`--vsock`) + snapshot/restore natively, confirmed from

the actual binary — so it does not have Firecracker's disqualifying virtiofs gap. It is the right microVM for our storage model.

  • Snapshot is near-instant (~0.15 s) and sparse (size ≈ touched RAM, not the RAM allocation).
  • `memory_restore_mode=ondemand` (userfaultfd) works once `vm.unprivileged_userfaultfd=1` (or CH

runs with `CAP_SYS_PTRACE`); this is the lazy-demand-paging mode that enables cheap fork fan-out.

  • Recommendation: GO. Adopt Cloud Hypervisor as the sandbox VMM for the fast-start / fork path,

converging #639 and #642 on one mechanism. Fork fan-out (N COW children from one warm base) has all required primitives confirmed present; the multi-child measurement is the one remaining PoC item.


1. Capability matrix (empirical — from the v53.0 binary, not docs)

CapabilityFirecrackerCloud Hypervisor v53.0Evidence
virtiofs (our global/inbox mounts)✗ none✓ `--fs tag=…,socket=…``cloud-hypervisor --help`: `--fs <fs>… virtio-fs parameters`
vsock (ADR-023 transport, #595)✓ `--vsock cid=…,socket=…``--vsock <vsock> Virtio VSOCK parameters`
snapshot / restore✓ `ch-remote snapshot` / `--restore``ch-remote` verbs `snapshot`, `restore`; `--restore source_url=…`
userfaultfd on-demand restore (fork enabler)✓ `memory_restore_mode=ondemand``--restore … memory_restore_mode=copy\ondemand` — "ondemand enables lazy demand paging (needs userfaultfd)"
qcow2 disk (direct)✗ (raw only)✓ `image_type=qcow2` (no backing chains)`--disk … image_type=<raw,qcow2,vhd,vhdx>`; standalone qcow2 only (overlays → `MaxNestingDepthExceeded`)
boot of existing Ubuntu imageneeds kernel+rootfs✓ via `hypervisor-fw` (rust-hypervisor-firmware)booted the agent base with `--kernel hypervisor-fw`

Firecracker is disqualified by the virtiofs row. Cloud Hypervisor satisfies every requirement.


2. Measured latency + size (agent base, 2 vCPU / 2048 MiB, `shared=on`)

PhaseCloud Hypervisor v53.0QEMU/q35 baseline (#639)
Boot → VM Running (firmware + qcow2)~0.5 s~22 s to guest-agent (boot only)
Snapshot (pause → write)0.06 s, sparse, size ≈ touched RAM (~141 MiB here)8–29 s (`virsh save`, ≈ touched RAM)
Restore → resumed (copy mode)0.134 s3.7–6.2 s (`virsh restore`)
Restore → resumed (ondemand/userfaultfd)0.043 s (~43 ms) — pages fault lazilyn/a (QEMU q35 copies eagerly)

Notes:

  • Snapshot layout: `config.json` (VM config incl. disk paths), `state.json` (device state ~53 KiB),

`memory-ranges` (a 2 GiB apparent file that is sparse — only ~141 MiB of touched pages consume disk). Restore latency is dominated by device-state setup, not RAM copy, which is why it is sub-second.

  • Guest here was a firmware-booted base without network/enrollment (host-side CH RSS ~147 MiB), so the

touched-RAM figure is a warm-idle floor; a loaded agent will snapshot larger, but restore latency stays low because ondemand pages fault lazily and copy-mode is already ~0.14 s at this footprint.

  • `ondemand` requires `sysctl vm.unprivileged_userfaultfd=1` or running the VMM with

`CAP_SYS_PTRACE`; otherwise restore fails `Failed to create userfaultfd: Operation not permitted`. This is an operational prerequisite to document, not a CH limitation.


3. Fork-from-warm-base

All primitives are confirmed present; the mechanism is:

  • Disk COW per child: each child gets its own writable disk. CH rejects qcow2 backing chains

(`MaxNestingDepthExceeded`), so children use standalone per-child disk copies (or raw + reflink on a CoW filesystem), not a qcow2 overlay of a shared base.

  • RAM isolation, not resident sharing: with `memory_restore_mode=ondemand`, userfaultfd reads the

shared `memory-ranges` snapshot input and uses `UFFDIO_COPY` to populate each child's distinct guest-memory memfd. A live N=2 inherited-memory mutation test on 2026-07-21 proved isolation, but measured 0 KiB defensible resident guest-RAM sharing. The shared snapshot inode/page cache must not be reported as shared resident guest RAM.

  • Per-child identity: each child needs a fresh vsock CID (#595) and independent enrollment

(#617/#619) — consistent with the "snapshot the pre-enrollment clean base, inject identity on restore" posture from #639 and the secret-hygiene work (#645).

Completed measurement: concurrent N=2 restore recorded 66 ms per child in the final controlled run. Each 4 GiB guest mapping reported 4 GiB RSS/PSS, distinct memfd inodes, 0 KiB KSM, and 0 KiB defensible resident sharing. The direct 64 MiB inherited-buffer mutations remained isolated. See `docs/research/evidence/ch-fork-memory-isolation-grissom-2026-07-21.json`.


4. Recommendation — GO

Adopt Cloud Hypervisor as the sandbox VMM for the fast-start / fork path, converging #639 and #642:

1. Runtime: CH v53.0 (or current), booting the agent base via `hypervisor-fw` with `--fs` (virtiofs global/inbox), `--vsock` (ADR-023), `shared=on` memory. 2. Fast resume / warm pool (#643 semantics on CH): snapshot a pre-enrollment clean warm base; restore per handout in ~0.14 s (copy) or ondemand; enroll on restore (fresh CID + secret + mTLS). 3. Fork fan-out: ondemand restore + per-child COW disk → many isolated children from one warm base, with full per-child resident-memory cost unless a separately measured deduplication mechanism is enabled. 4. Prereqs to bake into provisioning: `vm.unprivileged_userfaultfd=1` (or `CAP_SYS_PTRACE`); standalone (non-backing-chain) disks; per-child CID allocation. 5. Coexistence: CH is the fast/fork path; the current libvirt/QEMU path (and the `checkpoint-vm.sh` primitive from #643) remains for the existing q35 flow. Not migrating the libvirt path here (out of scope, #119/#120).


Appendix — reproduction

# capability probe (authoritative — the actual binary)
cloud-hypervisor --help | grep -E ' --fs| --vsock| --restore'
ch-remote --help | grep -iE 'snapshot|restore'

# boot agent base (flattened qcow2 — CH rejects backing overlays)
qemu-img convert -O qcow2 ubuntu-server-24.04-agent.qcow2 flat.qcow2
cloud-hypervisor --api-socket ch.sock --kernel hypervisor-fw \
  --disk path=flat.qcow2,image_type=qcow2 --cpus boot=2 --memory size=2048M,shared=on \
  --serial file=serial.log --console off &

# snapshot (URL is positional in v53)
ch-remote --api-socket ch.sock pause
ch-remote --api-socket ch.sock snapshot file:///path/snap

# restore (sub-second)
sysctl -w vm.unprivileged_userfaultfd=1     # for ondemand
cloud-hypervisor --api-socket r.sock \
  --restore source_url=file:///path/snap,memory_restore_mode=copy,resume=true &

References

  • #639 findings: `docs/research/memory-snapshot-restore-spike.md` (QEMU/q35 baseline).
  • #642 (sub-second start), #595 (vsock CID), #617/#619 (secrets), #643 (checkpoint primitive),

#645 (snapshot secret hygiene).

  • Cloud Hypervisor v53.0; rust-hypervisor-firmware 0.5.0.