Manifests
Status: this guide describes the plan-38 manifest model, shipped on
feat/manifest-driven-template-dx-claude. The user-facing primitive ismvm.toml; the oldmvmctl template *namespace has been removed.mvmctl manifest pushandpullare tracked in plan 39 and not yet implemented; everything else listed here works.
A manifest (mvm.toml or Mvmfile.toml) is the user-facing primitive for “what to build and how to size it”. One manifest sits next to a flake.nix in your project; together they describe one microVM.
my-service/├── mvm.toml # build inputs + dev sizing (this file)├── flake.nix # rootfs + kernel content (Nix's job)└── … # your app sourceThe flake is the source of truth for what’s inside the microVM. The manifest is the source of truth for how mvm builds and runs that flake — sizing, profile selector, optional display name. That’s the entire surface.
The 5-field schema
Section titled “The 5-field schema”flake = "." # default ".", any flake ref acceptedprofile = "default" # selects packages.<system>.<profile>vcpus = 2mem = "1024M"data_disk = "0"
name = "openclaw" # optional; display + S3 channel hintThat’s it. Build inputs (flake, profile) and dev sizing (vcpus, mem, data_disk). No role, no [network], no [[variants]], no dependencies — those are flake territory or mvmd territory, not the dev tool’s.
Each field’s owner:
| Field | Owner | In manifest? |
|---|---|---|
flake | mvmctl (input pointer) | Yes |
profile | flake defines, mvmctl selects | Yes, as selector |
vcpus | mvmctl — Firecracker host-side sizing | Yes |
mem | mvmctl — host-side sizing | Yes |
data_disk | mvmctl — host-side block device sizing | Yes |
name | mvmctl — display in ls, optional S3 channel key | Optional |
Anything not in this list belongs in the flake (kernel/rootfs content, NixOS modules, services) or in mvmd (multi-VM topology, network policy, secrets, runtime deps).
The everyday flow
Section titled “The everyday flow”Three commands. That’s the user model.
mvmctl init # scaffold mvm.toml + flake.nix in cwd$EDITOR mvm.toml # tweak sizing / profile to tastemvmctl build # discover manifest, run nix build, persist artifactsmvmctl up # boot the built microVMRepeated edits are just edits. The next mvmctl build re-reads mvm.toml and re-runs the build. Resource changes (vcpus, mem, data_disk) update silently; identity changes (flake, profile) trip a drift refusal that asks you to --force or rename — see Drift detection below.
Manifest discovery
Section titled “Manifest discovery”mvmctl build, mvmctl up, mvmctl run, mvmctl exec, mvmctl info, mvmctl rm all accept an optional [PATH] argument:
mvmctl build # walks up from cwd looking for mvm.tomlmvmctl build /abs/path/to/mvm.toml # explicit file pathmvmctl build /abs/path/to/project-dir # explicit directory (resolves to mvm.toml inside)Walk-up rules (Cargo-style): start at cwd, look for mvm.toml then Mvmfile.toml in each ancestor, stop at the first match, at a .git boundary, or at the filesystem root.
mvm.toml vs Mvmfile.toml
Section titled “mvm.toml vs Mvmfile.toml”Both filenames are accepted with the same parser and schema. Use whichever fits your repo’s convention. Two files in the same directory is an error ("found both mvm.toml and Mvmfile.toml in <dir>; pick one").
Scaffolding new projects
Section titled “Scaffolding new projects”mvmctl init creates a minimal mvm.toml + flake.nix in the target directory:
mvmctl init my-service # scaffold into ./my-servicemvmctl init # scaffold into cwdPresets
Section titled “Presets”mvmctl init my-api --preset python # Python HTTP servicemvmctl init my-web --preset http # generic HTTP servermvmctl init my-db --preset postgres # PostgreSQLmvmctl init my-job --preset worker # background worker / cron-likemvmctl init my-vm --preset minimal # bare minimum (default)Each preset emits a different flake.nix plus a mvm.toml with sensible resource defaults (vcpus = 2, mem = "1024M" for HTTP/Python, vcpus = 1, mem = "512M" for workers, etc.).
Prompt-driven scaffolding (LLM-assisted)
Section titled “Prompt-driven scaffolding (LLM-assisted)”mvmctl init my-api --prompt "FastAPI app with Postgres backend"A heuristic planner picks a preset from the prompt. With OPENAI_API_KEY set, an LLM refines the plan via structured output (JSON Schema, deterministic). With Ollama or another OpenAI-compatible local endpoint at 127.0.0.1:11434 or 127.0.0.1:8080, mvmctl auto-detects and uses it instead. Override via MVM_TEMPLATE_PROVIDER=auto|openai|local|heuristic.
The planner outputs a structured plan (preset, features, http port, entrypoint, resources) — no free-form Nix or shell. Generated flake.nix comes from a fixed preset corpus, not from the LLM.
Building
Section titled “Building”mvmctl build # discover manifest, buildmvmctl build --snapshot # also create a Firecracker snapshot where supportedmvmctl build --force # rebuild even if the cache hitsmvmctl build --update-hash # recompute Nix FOD hash (after package version bump)mvmctl build --vcpus 4 --mem 2G # CLI overrides; persisted to the slot recordBuild artifacts are stored in a content-addressed registry under ~/.mvm/templates/<sha256(canonical_manifest_path)>/artifacts/revisions/<revision_hash>/. The manifest’s path identifies the project; revision_hash = sha256(flake.lock + profile) content-addresses the actual build outputs.
Snapshots (--snapshot) are Firecracker-only. On Apple Virtualization or Docker the flag downgrades gracefully to image-only.
Listing / inspecting / removing
Section titled “Listing / inspecting / removing”Manifest registry operations live under mvmctl manifest. (The unprefixed mvmctl ls / mvmctl info / mvmctl down continue to operate on running VMs — those are unchanged.)
mvmctl manifest ls # list built slots (manifest path, name, last built)mvmctl manifest ls --json # machine-readablemvmctl manifest ls --orphans # slots whose manifest file is gonemvmctl manifest ls --legacy # pre-refactor name-keyed slots (migration aid)
mvmctl manifest info # details for the manifest at cwd / walked-upmvmctl manifest info /path/to/project # explicitmvmctl manifest info --json # full manifest + revision + provenance JSON
mvmctl manifest rm # remove the slot keyed by current manifestmvmctl manifest rm /path/to/project --force # idempotentmvmctl manifest rm --manifest-file # also delete mvm.toml on disk (off by default)For running VMs (separate concern), continue to use mvmctl ls / mvmctl down <vm> / mvmctl logs <vm> etc.
Booting
Section titled “Booting”mvmctl up # boot from slot keyed by manifest at cwdmvmctl up /path/to/project # explicitmvmctl exec /path/to/project -- uname -a # ephemeral one-shotIf no current revision exists, you get an error with a hint to run mvmctl build. If the manifest’s vcpus/mem differ from what the slot’s snapshot was taken at, the snapshot is ignored and a cold-boot from the rootfs proceeds (with a warning).
Backend mismatch
Section titled “Backend mismatch”If the slot was built on Firecracker but you boot on Apple Virtualization (or vice versa), mvmctl up warns and proceeds when artifacts are compatible (cold-boot from rootfs); hard-errors only when the artifact shape can’t be loaded.
Local registry inspection / cleanup
Section titled “Local registry inspection / cleanup”The mvmctl manifest * namespace is where slot-registry operations live:
mvmctl manifest verify # checksum integrity check (local)mvmctl manifest verify --revision <hash> # specific revisionmvmctl manifest prune --orphans # cleanup builds whose source mvm.toml is gonemvmctl manifest prune --orphans --dry-run # preview what would be removedmvmctl cache prune --orphan-builds is a convenience that bundles manifest prune --orphans into the broader cache-cleanup pass.
Sharing via a registry (planned)
Section titled “Sharing via a registry (planned)”Pushing a built slot to an S3-compatible registry and pulling it on another machine is planned but not yet implemented — the design is captured in plan 39. The dominant question (where pull installs the slot when the source’s manifest_path doesn’t exist on the target) is resolved there. The shape will be:
# producermvmctl manifest push [PATH] [--revision <hash>]
# consumermvmctl manifest pull <CHANNEL-OR-HASH> [DIR]mvmctl manifest pull openclaw ./openclaw # writes mvm.toml in DIR, installs artifactsmvmctl manifest verify --check-signature # cosign verify (gated on plan 36)Until plan 39 lands, transfer is via flake-level artifacts (Nix’s own caching + flake.lock). Most of the time that’s enough.
Drift detection
Section titled “Drift detection”The slot’s manifest.json records the manifest’s identity-shaping fields (flake, profile) at last build. If you edit mvm.toml to change either of those without --force, the next mvmctl build aborts with:
Manifest at
<path>declaresflake=X, profile=Y. The slot at<sha256>was last built withflake=X', profile=Y'. Pass--forceto overwrite, or pick a different manifest directory.
This catches typos, “I’m in the wrong cwd” mistakes, and accidental flake-ref churn. Resource changes (vcpus, mem, data_disk) update silently; only the build-identity fields trip the gate.
Schema versioning
Section titled “Schema versioning”mvm.toml carries an implicit schema_version = 1. Future fields are additive (default-valued), so older manifests keep parsing. Bumping the major schema version requires explicit opt-in:
schema_version = 2 # bumped manifestA manifest declaring schema_version higher than the running mvmctl supports errors with "this manifest declares schema_version=N; this mvmctl supports M; upgrade mvmctl".
What’s NOT in the manifest
Section titled “What’s NOT in the manifest”To keep the schema small and the boundaries crisp, the following are explicitly out:
- What’s installed in the rootfs → flake (via
mkGuest). - NixOS configuration / systemd services / users → flake.
- Kernel cmdline tweaks → flake (kernel package).
- Build-time deps on other flakes → flake
inputs+flake.lock. - Runtime deps on other VMs (lifecycle ordering, health gates) →
mvmd(separate repo). - Per-tenant network bridges, tap names, IP allocation →
mvmd. - Network egress policy →
mvmctl upflags or~/.mvm/config.tomldefaults; eventuallymvmdtenant config. - Secrets / env vars at boot →
mvmctl up-time injection ormvmdinstance config.
See also
Section titled “See also”- Nix flakes guide — writing the
flake.nixhalf of the equation - CLI reference — full flag/option list
- Plan 38 — the design doc this guide tracks