Skip to content

Two lanes

mvm has two halves of its test suite: the half that builds + runs unit tests on plain Linux, and the half that needs /dev/kvm to exercise Firecracker, the seccomp functional probe, and the live-KVM smoke target. mvm-deployment ships one lane for each.

Targets Hetzner Cloud (any tier). Runs cargo fmt, clippy with -D warnings, cargo test --workspace, cargo deny check, and cargo audit. Verified empirically May 2026 that no Hetzner Cloud tier exposes nested virt — the seccomp functional probe and any Firecracker-touching test is silently skipped.

Use this lane for fast iteration on logic that doesn’t depend on KVM.

Provider-agnostic. Targets anything exposing /dev/kvm:

  • GCP — nested virt on n2-standard-4 (and other N2 / C3 / M-series), via --enable-nested-virtualization.
  • AWS*.metal SKUs only. Standard EC2 with Nitro nested virt does not surface KVM to Firecracker.
  • Azure — Dv3+ tiers expose nested virt.
  • Vultr — Bare Metal SKUs (vbm-*).
  • Latitude.sh / Equinix Metal / OVH Bare Metal Cloud — true bare metal, hourly billing.

The cloud-init.yaml shipped here is provider-agnostic. The provider-specific quirks (SSH key injection, root volume sizing, AMI/image selection, region naming) live in providers/<name>.sh.

  1. Stage 1 — provision + cloud-init. up.sh invokes the provider adapter to create the instance, attaches cloud-init.yaml as user-data, then waits on SSH for cloud-init status: done. cloud-init is system-only: rustup, Nix, Firecracker, cargo-tools, KVM gate (hard-fail if /dev/kvm is absent), state dirs, SSH propagation. No clone, no build, no service install.
  2. Stage 2 — rsync + post-deploy. rsync local mvm + mvmd onto the box, ssh in, run post-deploy.sh. That builds mvmd in release mode, installs /usr/local/bin/{mvmd,mvm-hostd}, renders the TOML configs from in-repo templates with a fresh HMAC secret, copies the four mvmd systemd units, enables them, and writes /etc/mvm-deploy-status = ready.

The split exists because mvmd is private — the box can’t git clone it, so we rsync from the operator’s local checkout.

Every providers/<name>.sh must define exactly four functions:

Terminal window
provider_default_instance_type() # echoed default
provider_default_region() # echoed default
provider_up <label> <cloud-init> # creates instance, prints IP on stdout's last line
provider_down <label-or-empty> # destroys instance(s) tagged mvm-ephemeral=true

up.sh enforces the contract via declare -F checks at startup, so a broken adapter fails fast with a clear error before any cloud calls.