Architecture
operator workstation provisioned cloud host───────────────────── ──────────────────────~/mvm/ (public) ─┐ ├─rsync─→ /home/mvm/mvm/, /home/mvm/mvmd/~/mvmd/ (private) ┘ ↓ cargo build --release ↓ /usr/local/bin/{mvmd, mvm-hostd} ↓ systemctl enable --now mvm-hostd, mvmd-agent, mvmd-coordinator, mvmd-gatewayWhy the split
Section titled “Why the split”- mvm is the public Rust workspace — the core microVM runtime, CLI, guest, security crates, etc. Lives at tinylabscom/mvm.
- mvmd is the private daemon repo — coordinator, gateway, agent, runtime that hosts microVM tenants.
- mvm-deployment (this repo) is the operator-facing deploy + ops layer. It does not contain mvm or mvmd source — only the scripts, configs, and provider adapters that turn a fresh cloud host into a working live-KVM environment.
Two-stage deploy
Section titled “Two-stage deploy”The split between cloud-init and post-deploy.sh exists because mvmd is
private. The box can’t git clone it, so we rsync from the operator’s local
checkout after the system is set up.
Stage 1 — provision + cloud-init
Section titled “Stage 1 — provision + cloud-init”ops/ephemeral/up.sh invokes the provider adapter:
- Provider creates the instance with
cloud-init.yamlas user-data. up.shwaits on SSH forcloud-init status: done.
Cloud-init is system-only:
- Toolchain: rustup, Nix, Firecracker (pinned), cargo-fuzz / cargo-deny / cargo-audit
- KVM gate: hard-fail if
/dev/kvmis absent - State dirs:
/var/lib/mvm,/etc/mvmdwith correct ownership - System users:
mvm,mvmd - SSH key propagation: root → mvm
No clone, no build, no service install at this stage.
Stage 2 — rsync + post-deploy
Section titled “Stage 2 — rsync + post-deploy”After cloud-init reaches done:
rsyncmvm + mvmd onto the box (/home/mvm/mvm,/home/mvm/mvmd).- ssh in and run
ops/ephemeral/post-deploy.sh:cargo build --releasemvmd (each binary in its own workspace package)- install
/usr/local/bin/{mvmd,mvm-hostd} - render
/etc/mvmd/{coordinator,gateway}.tomlfrom in-repo templates with a fresh HMAC secret - install + enable the four mvmd systemd units
- write
/etc/mvm-deploy-status = ready
post-deploy.sh is idempotent: every step is [ -x bin ] && skip or
systemctl enable --now. Re-running up.sh against the same box is fast.
Provider abstraction
Section titled “Provider abstraction”Every providers/<name>.sh implements four functions:
provider_default_instance_type() # echoed defaultprovider_default_region() # echoed defaultprovider_up <label> <cloud-init> # creates instance, prints IP on stdout's last lineprovider_down <label-or-empty> # destroys instance(s) tagged mvm-ephemeral=trueup.sh and down.sh know nothing provider-specific — they source the chosen
providers/${PROVIDER}.sh and call those four functions. 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.
Conventions
Section titled “Conventions”- All shell scripts pass
shellcheckandshfmt -i 2 -ci. Adapters start with# shellcheck shell=bash. - All YAML parses cleanly:
python3 -c "import yaml; yaml.safe_load(open(...))". - All scripts use
set -euo pipefail. run()helper inops/hetzner/run-tests.shuseslocal rc=0; cmd || rc=$?becauseif cmd; then…fi; rc=$?has a known bash +set -einteraction bug that silently capturesrc=0on failure.