Skip to main content
Reproducible numbers for the claim: warm-fork a live multi-VM Kubernetes cluster in well under a second on the minimal guest and in about a second on a stock Ubuntu distro cluster, with TLS sessions surviving and the source cluster still answering. Every number below is an integration-test assertion, not a one-off benchmark paste. The budget constants live in crates/k7d/src/latency.rs (the single source of truth, imported by the tests) and are documented in LATENCY_BUDGETS.md. If a number here drifts from those files, trust the tests. Budgets are set at roughly 2× the typical observed value. Cluster-tree fork budgets are asserted on the daemon’s own latency_ns, not on the client’s round trip around the Unix socket. Tests print both; a client RTT over budget while the daemon stays under is a finding, not a failure.

Hardware

One pinned bare-metal node (~€40/month Hetzner) used for all figures:

Two clocks

Headline results — minimal CI guest

Inner-k3s add-on profiles

Same 3-VM fixture with one stock add-on switched on, plus load churn. Deltas are against the ~104 ms lean baseline. All share the 1 s budget except ArgoCD, which gets its own 2 s.

Headline results — Ubuntu nodes

Stock Ubuntu 24.04 root disk, systemd, distro kernel; 3 VMs at 2 GiB / 2 vCPU unless noted. Boot ceilings are loose hang detectors (600 s), not 2× typical. Every fork row reuses one 1 s ceiling, which is also the sub-second target; missing it is a recorded finding, not a silent pass. Six CoW disks and twice the guest RAM (Longhorn) still fork inside the ceiling the 2 GiB clusters were measured against: fork cost tracks the number of devices and page tables, not the volume of storage behind them. The pull is the long pole every time, not the fork.

Keyframes: O(divergence)

A fork point below the root clones the nearest ancestor’s memory.bin with FICLONE and writes only that node’s dirty pages. 3 × 2 GiB Ubuntu k3s, 30 s of divergence each; ceiling 4.5 s (~2× the worst level): Test: test_keyframe_cost_ubuntu_divergence_levels. The bytes are the daemon’s base_disk_bytes; du and stat cannot measure a reflinked file. Neither the keyframe nor the full dump it replaces is fsynced — syncing a patched reflinked file costs 2–5 s per member because every written page breaks a shared extent.

Time warp

An 8.7-minute guest-time episode on a fresh 3-node Ubuntu k3s cluster, three runs per arm (make warp-bench, a Criterion bench kept out of make check for its runtime): The dilation N sweep (compression exactly N through 16; extra probe failures 0 / 0 / 0 / 3 / 19 / 32 for N ∈ ) and the fork-of-a-dilated-VM tests need the carried KVM patch loaded (make dilation-install; utils/kvm-dilation/) and are kept out of make check. See Time warp.

Full budget table (minimal guest)

Reproduce

On a node with the repo and toolchain installed (see HACKING.md for the remote loop):
The inner-k3s test prints a line like:

How the fast path was won

Before the block-rootfs work, cluster pods carried a virtiofs share for the k3s hostPath — so every fork forced a full memory copy of 3 × ~3.2 GiB, landing at ~2 s typical. Moving read-only hostPath mounts to cached erofs images on virtio-blk (zero virtiofs shares) let every member fork MAP_PRIVATE + dirty-page CoW: ~105 ms under a 1 s budget. The share-less fork prep also preserves the source’s vsock CID, so kubectl exec against the source still works after adopt/fork. The Ubuntu clock is a different guest — systemd and a full distro rootfs instead of a busybox initramfs — through the same VMM. Its root disk forks as a reflink on the daemon’s XFS (~2500 shared extents per child), so the fork is CoW on disk as well as memory.

Comparison (honest)

What we are not claiming

  • Firecracker-beating cold boot (we boot once, then fork).
  • “<5 MB per instance” density (our guests run kubelet).
  • That the virtiofs era (~2 s full-copy) was the intended steady state — it is now the explicit fallback for read-write hostPath only.
  • That dilation is free — it is a measured, bounded fidelity cost, and the jump policy exists because it has none.