Guides
Sandboxed Exec
Run a single command inside a fresh microVM and tear it down on exit.
Running a single command in a fresh transient microVM is the
mvmctl machine run -- <cmd> workflow: it boots a microVM from a source you
name (--image, --flake, or --manifest), runs one command via the guest
agent, streams stdout/stderr back to your terminal, propagates the exit code,
and tears the VM down — success, failure, or Ctrl-C.
Think docker run --rm, but with a microVM as the isolation boundary.
mvmctl machine run --image alpine -- uname -amvmctl machine run --flake . --mount .:/work -- ls /workmvmctl machine run --manifest my-tpl -- /bin/trueOverriding the guest’s argv (a trailing
-- <cmd>) is a dev-tier capability. It requires DevOnly verbs regardless of the image’s sealed bit; sealed production images also refuse it because their entrypoint is fixed. Use--image/--flakedev builds and a dev profile for ad-hoc commands; production workloads run their baked entrypoint (machine run --entrypoint) or go throughmvmd.
When to use it
Section titled “When to use it”- Reach for a transient
mvmctl machine run -- <cmd>when you want to run an untrusted binary, a build script, an LLM-generated command, or any one-shot task that benefits from a strong isolation boundary but doesn’t justify a long-running VM. - Reach for a persistent
mvmctl machine run --name <n> -dwhen you want a VM you can re-enter, share state with, or forward ports from. - Reach for
mvmctl machine exec <n> -- <cmd>when you already have a named VM running and want to run something inside it without a fresh boot.
Choosing a source
Section titled “Choosing a source”mvmctl machine run always boots from a source you name — there is no bundled
default image:
--image <ref>— an OCI image, pulled and cached (no host Nix, no flake). The fastest path for ad-hoc commands.--flake <ref>— a Nix flake built in the builder VM. Customize the guest withmvm.lib.<system>.mkGuest(see Building MicroVM Images + Dev Image).--manifest <name>— a pre-built manifest slot or registered template, which skips the build step entirely.
Sharing host directories: --mount
Section titled “Sharing host directories: --mount”--mount HOST:GUEST[:MODE] shares a host directory into the guest at
GUEST. MODE is ro (default) or rw; a writable share requires
--profile dev or --profile permissive. The flag is repeatable. --volume
remains accepted as a compatibility alias, but -v is global verbosity.
Read-only (default)
Section titled “Read-only (default)”echo "hello" > /tmp/foomvmctl machine run --image alpine --mount /tmp:/host -- cat /host/foo # prints "hello"Writable: :rw
Section titled “Writable: :rw”mvmctl machine run --flake . --profile dev --mount .:/work:rw -- sh -c 'echo result > /work/output.txt'cat ./output.txt # "result" — written by the guestA writable share lets the guest edit host files under GUEST — exactly what
you want for a coding agent that needs to edit your repo. For the durability
and host-visibility semantics of the current volume backend, see the
machine volume docs.
Multiple shares
Section titled “Multiple shares”Modes are independent per directory:
mvmctl machine run --flake . --profile dev \ --mount ./src:/work:rw \ --mount ~/.cargo:/root/.cargo:ro \ -- cargo build --manifest-path /work/Cargo.tomlInjecting environment variables: --env
Section titled “Injecting environment variables: --env”mvmctl machine run --image alpine -e FOO=bar -e BAZ=qux -- env | grep -E '^(FOO|BAZ)='--env (or -e) is repeatable. When used together with --launch-plan,
CLI --env overrides any env vars the launch plan carries (see below).
Snapshot restore (registered templates)
Section titled “Snapshot restore (registered templates)”When you pass --manifest <name> and that template has a compatible recovery
artifact, mvmctl machine run may use the backend’s advertised recovery tier
instead of cold-booting. The tier is backend-specific; inspect mvmctl doctor
before relying on its latency or fidelity.
The snapshot path activates only when:
- the image source is a registered template (an OCI image or ad-hoc flake has no template snapshot to restore from), AND
- the request has no
--mountextras (extra drives would mismatch the snapshot’s recorded layout), AND - the active backend reports snapshot support.
Unsupported recovery requests return an actionable typed error. mvm does not
silently downgrade a live-memory or machine-state request to disk-only recovery
or cold boot. The harder branch — parameterized snapshots that allow
--mount — is tracked in issue #7.
Resource controls
Section titled “Resource controls”mvmctl machine run --flake . --cpus 4 --memory 1G -- ./benchmark.shmvmctl machine run --flake . --timeout 300 -- ./long-running-task.shDefaults: 2 vCPUs, 512 MiB, 60-second timeout per command.
Driving from a launch plan
Section titled “Driving from a launch plan”mvmctl run --launch-plan <path> accepts either of two JSON
shapes — a launch.json artifact (top-level entrypoint) or a
Workload IR manifest (top-level apps[]) — and auto-detects which
one it is given. Both shapes were historically produced by the
mvmforge toolchain
(see the migration guide);
the canonical producer today is mvmctl build compile in the mvm SDK.
mvmctl build compile manifest.json --out ./buildmvmctl run --launch-plan ./build/launch.jsonOnly the entrypoint is consumed in v1; image selection still comes from
--manifest/--image/--flake.
LaunchPlan artifact (top-level entrypoint):
{ "artifact_format_version": "1.0", "workload_id": "hello", "entrypoint": { "command": ["python", "main.py"], "working_dir": "/app", "env": { "PORT": "8080" } }, "env": { "LOG_LEVEL": "info" }}Workload IR manifest (top-level apps[]):
{ "apps": [ { "name": "hello", "entrypoint": { "command": ["python", "main.py"], "working_dir": "/app", "env": { "PORT": "8080" } }, "env": { "LOG_LEVEL": "info" } } ]}For long-running workloads, prefer mvmctl machine run --flake <artifact-dir>:
the SDK bakes the entrypoint into the generated flake’s
services.<id>.command, and mvm’s PID-1 init supervises it across
reboots.
Multi-app launch plans are rejected — that’s an orchestration concern
that belongs in mvmd, not in mvmctl machine run. Env precedence (lowest →
highest):
apps[].envapps[].entrypoint.env- CLI
--env(always wins)
--launch-plan is mutually exclusive with a trailing argv.
Teardown semantics
Section titled “Teardown semantics”- Normal exit: VM is stopped and the staging dir for
--mountimages is cleaned up. - Non-zero exit: same as normal exit;
mvmctl machine runpropagates the guest’s exit code. - Ctrl-C: a SIGINT handler triggers teardown so the Firecracker process and any tap interface don’t get orphaned.
- Hard kill (
kill -9onmvmctl machine runitself): teardown is best-effort; you may needmvmctl machine lsandmvmctl machine stop <name>to clean up. Each unnamed transient VM gets a generated name likebrisk-otter-a1b2, so it is easy to spot.
Limits
Section titled “Limits”- Ad-hoc execution is dev-mode only. A baked-entrypoint
mvmctl machine runmay use the restricted ProdSafe grant on a non-dev profile. A trailing argv requires the guest agent’sinteractiveCargo feature and DevOnly verbs; production guest images may omit that feature, in which case the Exec handler is physically absent from the binary. - Network access. The guest gets the same network configuration
any other transient VM gets — if your
--manifestexposes outbound internet, so doesmvmctl machine runfrom that template. - Stdin is currently not forwarded to the guest. Pipe data via a
--mount-shared file instead. Streaming stdin is a future improvement. - Persistent state doesn’t survive teardown beyond what
:rw--mountrsyncs back. For larger or longer-lived state, usemvmctl machine runwith a persistent volume.
See also
Section titled “See also”- CLI reference: One-shot Exec
- Manifests guide — build a reusable base image
via
mvm.toml;mvmctl machine run [PATH]accepts the manifest path directly - Quick Start