Config & Secrets Injection
mvm supports injecting custom files onto the guest’s config and secrets drives at boot time. Files are written to the drive images before the VM starts.
Prefer managed secret references for credentials. Use secrets drives when a workload genuinely needs file-shaped material such as certificates or a compatibility config file. See Secrets and credentials for the reference-first model.
CLI Usage
Section titled “CLI Usage”mkdir -p /tmp/my-config /tmp/my-secrets
echo '{"gateway": {"port": 8080}}' > /tmp/my-config/app.jsonecho 'API_KEY_REF=openai-api-key' > /tmp/my-secrets/app.env
mvmctl up --manifest my-app \ --volume /tmp/my-config:/mnt/config \ --volume /tmp/my-secrets:/mnt/secretsThe --volume (-v for short) flag uses the format host_dir:/guest/path:
| Guest path | Drive | Permissions | Purpose |
|---|---|---|---|
/mnt/config | /dev/vdb | Read-only (0444) | Application configuration |
/mnt/secrets | /dev/vdc | Read-only (0400) | File-shaped secret material |
Every file in the host directory is written to the corresponding drive image. For persistent volumes with explicit size, use the 3-part format: --volume host:/guest/path:size.
Library API
Section titled “Library API”The same functionality is available programmatically for library consumers:
use mvm_runtime::vm::microvm::{DriveFile, FlakeRunConfig};
let config = FlakeRunConfig { config_files: vec![DriveFile { name: "app.json".into(), content: serde_json::to_string(&app_config)?, mode: 0o444, }], secret_files: vec![DriveFile { name: "app.env".into(), content: format!("API_KEY={}", api_key), mode: 0o400, }], ..base_config};Managed Secrets
Section titled “Managed Secrets”mvmctl up --secret has been removed.
Use mvmctl secret put to store local secret refs, then bind those refs
through mvm.toml or the SDKs. That is the supported path for managed
secrets.
The managed-secret model is:
- Store a secret ref locally with
mvmctl secret put <name> - Declare that ref in
mvm.tomlor withmvm.secret(...) - The guest sees only a normal env var name with an opaque token
- Host-mediated surfaces such as
mvm.web_fetchandmvm.web_searchrelease the real value at request time when policy allows it
Managed secret refs are host-mediated only. Guest HTTPS CONNECT egress is not a substitution path.
Design
Section titled “Design”The DriveFile type is content-agnostic — it’s just {name, content, mode}. It knows nothing about specific file formats or keys. This means:
- Any file format works (JSON, TOML, YAML, env files, certificates, etc.)
- Adding support for new applications doesn’t require code changes
- NixOS
EnvironmentFilecan load.envfiles directly as systemd environment variables
Example: generic flake with config + secrets mounts
Section titled “Example: generic flake with config + secrets mounts”The pattern below works with any mkGuest flake that reads
/mnt/config/ and/or /mnt/secrets/ at boot. Write your own — see
Building MicroVM Images for the
mkGuest API surface, or Nix Flakes for a
worked LLM-agent example showing the pattern end-to-end.
Running with host-mounted config and secrets
Section titled “Running with host-mounted config and secrets”mvmctl build ./openclawmvmctl up ./openclaw --name oc \ -v nix/examples/openclaw/config:/mnt/config \ -v nix/examples/openclaw/secrets:/mnt/secrets \ -p 3000:3000mvmctl forward my-vm 3000:3000Each -v flag mounts a host directory as an ext4 drive read-only by
default. Secrets land at /mnt/secrets/ (mode 0440 root:mvm by the
init script) and are also re-staged to /run/mvm-secrets/<svc>/
with mode 0400 owned by the per-service uid (ADR-002 §W2.1) so
sibling services on the same microVM can’t cross-read.
Custom config + API keys at runtime
Section titled “Custom config + API keys at runtime”# Create a config directory with whatever shape your flake expectsmkdir -p /tmp/my-configcat > /tmp/my-config/app.json << 'EOF'{ "feature_flag": "value" }EOF
# Create a secret-reference file the service understandsmkdir -p /tmp/my-secretscat > /tmp/my-secrets/secret-refs.env << 'EOF'ANTHROPIC_API_KEY_REF=anthropic-api-keyEOF
mvmctl up ./openclaw --name oc \ -v /tmp/oc-config:/mnt/config \ -v /tmp/oc-secrets:/mnt/secrets \ -p 3000:3000A typical mkGuest service uses preStart to check for
/mnt/config/<file> and falls back to a built-in default; the
command script sources /mnt/secrets/<env-file> if present so
environment variables are available to the service process.
Using snapshots for faster startup
Section titled “Using snapshots for faster startup”Build the manifest with --snapshot on a backend that supports it to capture a
running VM state. Subsequent runs can restore from the snapshot instead of
cold-booting. Published latency numbers must name the backend, host, artifact,
and readiness boundary.
mvmctl build ./openclaw --snapshotmvmctl up ./openclaw --name oc \ -v nix/examples/openclaw/config:/mnt/config \ -v nix/examples/openclaw/secrets:/mnt/secrets \ -p 3000:3000When restoring from a snapshot with -v mounts, the guest agent
automatically remounts config/secrets drives and restarts services
with the fresh data.
Snapshots + dynamic mounts
Section titled “Snapshots + dynamic mounts”Key insight: the snapshot stores OS and application state (memory, running processes, compiled code caches), but config and secrets drives are created fresh at runtime from your host directories. This means:
- ✅ Same snapshot can serve multiple instances with different configs.
- ✅ Update configs without rebuilding — change the host files and re-up.
- ✅ Instant boot + dynamic configuration — get both benefits simultaneously.
Example: run three instances from one snapshot with different API keys:
# Production gateway with prod Anthropic keymvmctl up --manifest openclaw --name oc-prod \ -v ./prod/config:/mnt/config \ -v ./prod/secrets:/mnt/secrets \ -p 3000:3000
# Staging gateway with test keymvmctl up --manifest openclaw --name oc-staging \ -v ./staging/config:/mnt/config \ -v ./staging/secrets:/mnt/secrets \ -p 3001:3000
# Dev gateway with no key (localhost-only testing)mvmctl up --manifest openclaw --name oc-dev \ -v ./dev/config:/mnt/config \ -p 3002:3000All three restore from the same snapshot (1-2 second boot) but get different configs and secrets at runtime.
Monitoring the VM
Section titled “Monitoring the VM”mvmctl logs my-vm # view console outputmvmctl logs my-vm -f # follow in real time