bsdkrun CLI
bsdkrun runs lightweight microVMs (FreeBSD / NetBSD / Linux OCI images) on macOS
(Hypervisor.framework) and Linux (KVM), built on libkrun. It's Docker-like:
run → ps → exec/shell → stop → start → rm, plus machine snapshots
(snapshot/branch/restore), reusable flavors (commit),
persistent volumes, port forwards, an in-guest agent (for exec/ssh/tailscale),
and optional global networks with internal DNS.
State lives under <state>/ ($XDG_STATE_HOME/bsdkrun or ~/.local/state/bsdkrun);
downloads cache under $BSDKRUN_CACHE (default ~/.cache/bsdkrun).
For the full flag list of every command, read references/cli-reference.md.
Command map
Run a guest:
bsdkrun linux <IMAGE> [-- CMD...] — boot an OCI image (Docker Hub / any registry).
bsdkrun freebsd [-- CMD...] — FreeBSD (EFI boot on macOS, PVH on Linux/amd64).
bsdkrun netbsd [-- CMD...] — NetBSD (direct-kernel boot).
bsdkrun kernel / bsdkrun firmware — low-level boot from a kernel or UEFI image + disk.
Lifecycle:
bsdkrun ps [-a] [--json] — list machines (running, or all with -a).
bsdkrun stop <id> — stop a running machine (BSD guests clean-poweroff first).
bsdkrun start <id> — restart a stopped machine in place (same id/disk, resumes its data).
bsdkrun update <id> [--cpus N] [--mem M] — change recorded vCPU/RAM (applies on next start).
bsdkrun rm [-f] <id>... — remove machine(s) and their state (-f stops first).
Interact:
bsdkrun exec [-t] [-e K=V]... <id> <cmd>... — run a command in a guest (via its agent).
bsdkrun cp [-r] <SRC> <DST> — copy files host<->guest, docker cp-style (ID:PATH, - = stdio).
bsdkrun cache save <id>:<path> --key K [-c gzip|zstd|estargz|none] — archive a guest dir.
bsdkrun cache restore <id>[:<path>] --key K [--restore-keys PREFIX...] — put it back (a miss exits 0).
bsdkrun cache ls / bsdkrun cache rm <key>... | --all — list and remove entries.
bsdkrun doctor [--json] — check the host can run machines; exits 1 on any failure.
bsdkrun shell <id> — attach an interactive console to a detached machine.
bsdkrun logs [-f] [--boot] <id> — show the console log (--boot = bsdkrun's own boot log).
AI coding agents (sandboxed, one microVM each):
bsdkrun claude — Claude Code in a sandbox that shares the current directory; also
codex, gemini, opencode, crush, copilot, kilo, qwen.
bsdkrun ai agents [--json] — the agents, and whether each one's image is built.
bsdkrun ai ls [--json] — sandboxes, grouped by project.
bsdkrun ai start <agent> [--workspace PATH] [--repo URL] [--name N] [--project P] [--new] [-d]
bsdkrun ai stop|rm <agent> — rm also drops the saved login unless --keep-home.
- Per-agent
$HOME volume (login persists), ~/.agents/skills shared into every
sandbox, host git identity + read-only ~/.ssh injected, and git/Docker/Nix
preinstalled. --no-workspace shares nothing; --no-ssh withholds the keys.
bsdkrun ai resume <machine> [-d] — bring ONE stopped sandbox back (keeps its
workspace/name/project) and wait for its guest agent; ai start would boot a second one.
bsdkrun ai disk [ls --watch [SECS]] — the shared Docker/Nix store disks (one running
holder at a time), per-sandbox usage, and host free space. ai disk grow docker --size 200G.
bsdkrun ai upload [--what skills|ssh|git|workspace] [--agent A] [DIR] — copy local
skills / keys / git identity / a project onto a REMOTE engine's sandbox ($HOME and
system dirs are refused; .gitignore/.dockerignore respected, --all overrides).
- Paths resolve on the engine's host — for a remote daemon use
--repo or ai upload.
CI (tangled spindle workflows in microVMs):
bsdkrun ci run [names...] — run .tangled/workflows/*.yml locally: one microVM per
workflow (nixery.dev image from its dependencies:), clone of HEAD, steps streamed.
Flags: --event push|pull_request|manual, -f FILE (explicit file, skips matching),
-w DIR, --input k=v, --keep (keep the VM after a failure), --json (spindle
LogLine JSON). Runs the HEAD commit, never the dirty tree.
bsdkrun ci ls [--event E] — workflows and whether each matches that trigger.
bsdkrun ci serve [--bind H:P] — accept sh.tangled.pipeline records over HTTP and
run them (a spindle-compatible runner for a server).
- Every SDK can define workflows in code (
workflow("test").on_push("main").step(...))
and .run() them — YAML is generated, never hand-written.
Docker (a Docker engine in a microVM, driven by the host's own docker CLI):
bsdkrun docker start [--cpus N] [--mem M] [--disk-size 60G] [--mount PATH] — boot (or
resume) the engine, serve its API on a host unix socket, and point docker at it via a
bsdkrun context. One VM, always named bsdkrun-docker; a second start resumes it.
bsdkrun docker status [--json] / bsdkrun docker stop / bsdkrun docker rm [-f].
bsdkrun docker ps [-a] [--json] — containers. bsdkrun docker logs <c> [--tail N].
bsdkrun docker container <start|stop|restart|kill|pause|unpause|rm> <c>....
bsdkrun docker disk [--size 100G] — show or grow the image store.
bsdkrun docker env — the DOCKER_HOST line, for a shell not using the context.
bsdkrun docker shell — a shell in the engine VM (not in a container).
- Published container ports are mirrored onto the host automatically;
$HOME is shared
into the VM at the same path so -v $PWD:/app resolves.
Snapshots (a machine's disk state, copy-on-write):
bsdkrun snapshot <id> [name] [-d DESC] — capture a machine's disk state (BSD guests are powered
off first, and left stopped; Linux guests keep running).
bsdkrun snapshots [machine] [--json] — list snapshots, newest first.
bsdkrun snapshot rm <name>... — delete snapshots and their data.
bsdkrun branch <snapshot|machine> [-d] [--name N] [--port H:G] — boot a NEW machine from a
snapshot; naming a machine snapshots it first. Prints the new machine's id.
bsdkrun restore <id> <snapshot> [-f] — put a machine back to a snapshot (-f stops it first;
the replaced state is snapshotted first unless --no-backup). Left stopped — start to run it.
bsdkrun rollback <id> [-f] — restore to the machine's most recent snapshot.
Flavors (reusable environments):
bsdkrun commit <id> <name> — freeze a machine into a flavor (like docker commit).
bsdkrun flavors [--json] — list the catalog + your snapshots.
bsdkrun flavor run [-d] <name> [--port H:G] [-v NAME] — boot a machine from a flavor.
bsdkrun flavor add --base <ref> [--nix PKG] [--provision CMD]... <name> — define a custom flavor.
bsdkrun flavor build <name> — pre-build a flavor's provisioned rootfs into the cache.
bsdkrun flavor rm <name> — remove a snapshot / user flavor.
Global networks (shared subnet + internal DNS — like docker-compose):
bsdkrun network create <name> — create a network (starts its shared gvproxy).
bsdkrun network ls [--json] — list networks + member counts.
bsdkrun network rm [-f] <name>... — delete network(s).
bsdkrun network connect <machine> <network> — join/switch a machine (applies on next start).
bsdkrun network disconnect <machine> — detach back to isolated (applies on next start).
bsdkrun network sync <network> — refresh members' /etc/hosts so peers resolve by name.
- Join at boot with
--network <name> [--name <member>] on linux/freebsd/netbsd.
Remote access (in-guest agent actions):
bsdkrun ssh <id> setup|add-key|status [--user U] [--key K]... — key-based SSH.
bsdkrun tailscale <id> setup|status|install|start [--authkey K] [--hostname H] — tailscale.
bsdkrun systemd <id> setup|status|disable — systemd as PID 1 (Linux; boot on -v to persist).
bsdkrun agent update <id> — refresh a stale baked-in guest agent.
Disks, images, volumes:
bsdkrun prune [--all] [--volumes] [--only KIND]... [-f] [--dry-run] — reclaim disk:
stopped machines, unused images, orphaned rootfs trees. Asks with a summary first;
--only orphan is the selection that cannot cost anything. --all adds the OCI layer
cache; --volumes adds unused volumes.
bsdkrun images [--json] — list downloaded images.
bsdkrun image rm <id>... [-f] — remove dangling images (refused while a machine uses one).
bsdkrun volume ls | rm <name>... — manage persistent volumes.
bsdkrun fetch [--os freebsd|netbsd] [--version V] — download + prepare a BSD image.
bsdkrun versions — list downloadable BSD builds.
bsdkrun grow --disk <path> --size <8G> — enlarge a disk image (guest expands on next boot).
bsdkrun probe — check that libkrun links and a VM context can be created.
Common flags on run commands (linux/freebsd/netbsd)
-d, --detach — run in background, print the id (like docker run -d).
--name <NAME> — machine name (also its DNS name on a --network).
--network <NAME> — join a global network.
--cpus <N> / --mem <MiB> — resources (defaults 1 / 512).
--port <HOST:GUEST> — forward a host TCP port (repeatable), e.g. --port 2222:22.
-v, --volume <NAME> — persist the rootfs/disk to a named volume.
--attach-disk PATH[:ro] — attach an extra raw disk image as virtio-blk (repeatable).
--no-net — boot with no NIC (disables the agent → no exec/shell).
--repo <URL> — clone a git repo into the guest and cd into it on shell open.
- Linux only:
--mount HOST:GUEST[:ro], --entrypoint, -e K=V, --initramfs, --kernel(-version).
- BSD only:
--version, --persist, --disk-size 8G, --verbose.
Key behaviors to remember
start resumes the machine's own storage (its disk / rootfs), not a fresh base
image — snapshot and runtime data survive stop/start.
stop cleanly powers off BSD guests (shutdown -p now) so their UFS is
consistent; it takes a few seconds. Linux writes go straight to the host FS.
- NetBSD name resolution relies on
/etc/hosts sync (its resolver rejects the
gvproxy DNS's AAAA NXDOMAIN). Joins auto-sync; use network sync to refresh an
existing network without restarting members.
- Snapshots of BSD guests power the guest off first for a clean, bootable image.
- Linux storage on macOS is case-sensitive. The first Linux launch automatically creates a
sparse APFS store for OCI roots and volumes. Inspect or manage it with
bsdkrun store status,
init, attach, detach, and rm.
- The desktop app mirrors these: Machines, Images, Volumes, Flavors, and Networks
views, plus per-machine edit (CPU/RAM, network) and network member browsing.
Examples
# Ephemeral Alpine, run one command
bsdkrun linux alpine -- echo hi
# Detached Ubuntu with SSH forwarded, then exec into it
id=$(bsdkrun linux -d --name web --port 2222:22 ubuntu:24.04 -- sleep infinity)
bsdkrun exec -t "$id" bash
bsdkrun ssh "$id" setup # installs your ~/.ssh/id_*.pub
# FreeBSD, persistent, on a named volume
bsdkrun freebsd -d --name bsd -v bsddata --disk-size 8G
# Two machines on one network, reachable by name
bsdkrun network create devnet
bsdkrun linux -d --network devnet --name db postgres
bsdkrun linux -d --network devnet --name api myapi # api can `ping db`
# Snapshot a configured machine and reuse it
bsdkrun commit web my-web-env
bsdkrun flavor run -d my-web-env
1---2name: bsdkrun-cli3description: Reference for the bsdkrun CLI — a Firecracker-style microVM launcher for FreeBSD, NetBSD, and Linux (OCI) guests on macOS/Linux, built on libkrun. Also replaces Docker Desktop (`bsdkrun docker` runs a Docker engine in a microVM and serves its API on a host socket, so the normal `docker`/`compose`/`buildx` CLIs drive it) and sandboxes AI coding agents (`bsdkrun claude`, `codex`, `gemini`, … each in its own microVM). Use when running, managing, networking, snapshotting, or troubleshooting bsdkrun machines or its Docker engine, or when writing commands/scripts against the CLI. Covers every subcommand and its flags.4license: MIT5---67# bsdkrun CLI89`bsdkrun` runs lightweight microVMs (FreeBSD / NetBSD / Linux OCI images) on macOS10(Hypervisor.framework) and Linux (KVM), built on **libkrun**. It's Docker-like:11`run → ps → exec/shell → stop → start → rm`, plus machine snapshots12(`snapshot`/`branch`/`restore`), reusable flavors (`commit`),13persistent volumes, port forwards, an in-guest agent (for `exec`/`ssh`/tailscale),14and optional **global networks** with internal DNS.1516State lives under `<state>/` (`$XDG_STATE_HOME/bsdkrun` or `~/.local/state/bsdkrun`);17downloads cache under `$BSDKRUN_CACHE` (default `~/.cache/bsdkrun`).1819**For the full flag list of every command, read `references/cli-reference.md`.**2021## Command map2223Run a guest:24- `bsdkrun linux <IMAGE> [-- CMD...]` — boot an OCI image (Docker Hub / any registry).25- `bsdkrun freebsd [-- CMD...]` — FreeBSD (EFI boot on macOS, PVH on Linux/amd64).26- `bsdkrun netbsd [-- CMD...]` — NetBSD (direct-kernel boot).27- `bsdkrun kernel` / `bsdkrun firmware` — low-level boot from a kernel or UEFI image + disk.2829Lifecycle:30- `bsdkrun ps [-a] [--json]` — list machines (running, or all with `-a`).31- `bsdkrun stop <id>` — stop a running machine (BSD guests clean-poweroff first).32- `bsdkrun start <id>` — restart a stopped machine in place (same id/disk, resumes its data).33- `bsdkrun update <id> [--cpus N] [--mem M]` — change recorded vCPU/RAM (applies on next `start`).34- `bsdkrun rm [-f] <id>...` — remove machine(s) and their state (`-f` stops first).3536Interact:37- `bsdkrun exec [-t] [-e K=V]... <id> <cmd>...` — run a command in a guest (via its agent).38- `bsdkrun cp [-r] <SRC> <DST>` — copy files host<->guest, `docker cp`-style (`ID:PATH`, `-` = stdio).39- `bsdkrun cache save <id>:<path> --key K [-c gzip|zstd|estargz|none]` — archive a guest dir.40- `bsdkrun cache restore <id>[:<path>] --key K [--restore-keys PREFIX...]` — put it back (a miss exits 0).41- `bsdkrun cache ls` / `bsdkrun cache rm <key>... | --all` — list and remove entries.42- `bsdkrun doctor [--json]` — check the host can run machines; exits 1 on any failure.43- `bsdkrun shell <id>` — attach an interactive console to a detached machine.44- `bsdkrun logs [-f] [--boot] <id>` — show the console log (`--boot` = bsdkrun's own boot log).4546AI coding agents (sandboxed, one microVM each):47- `bsdkrun claude` — Claude Code in a sandbox that shares the current directory; also48 `codex`, `gemini`, `opencode`, `crush`, `copilot`, `kilo`, `qwen`.49- `bsdkrun ai agents [--json]` — the agents, and whether each one's image is built.50- `bsdkrun ai ls [--json]` — sandboxes, grouped by project.51- `bsdkrun ai start <agent> [--workspace PATH] [--repo URL] [--name N] [--project P] [--new] [-d]`52- `bsdkrun ai stop|rm <agent>` — `rm` also drops the saved login unless `--keep-home`.53- Per-agent `$HOME` volume (login persists), `~/.agents/skills` shared into **every**54 sandbox, host git identity + read-only `~/.ssh` injected, and git/Docker/Nix55 preinstalled. `--no-workspace` shares nothing; `--no-ssh` withholds the keys.56- `bsdkrun ai resume <machine> [-d]` — bring ONE stopped sandbox back (keeps its57 workspace/name/project) and wait for its guest agent; `ai start` would boot a second one.58- `bsdkrun ai disk [ls --watch [SECS]]` — the shared Docker/Nix store disks (one running59 holder at a time), per-sandbox usage, and host free space. `ai disk grow docker --size 200G`.60- `bsdkrun ai upload [--what skills|ssh|git|workspace] [--agent A] [DIR]` — copy local61 skills / keys / git identity / a project onto a REMOTE engine's sandbox ($HOME and62 system dirs are refused; `.gitignore`/`.dockerignore` respected, `--all` overrides).63- Paths resolve on the *engine's* host — for a remote daemon use `--repo` or `ai upload`.6465CI (tangled spindle workflows in microVMs):66- `bsdkrun ci run [names...]` — run `.tangled/workflows/*.yml` locally: one microVM per67 workflow (nixery.dev image from its `dependencies:`), clone of HEAD, steps streamed.68 Flags: `--event push|pull_request|manual`, `-f FILE` (explicit file, skips matching),69 `-w DIR`, `--input k=v`, `--keep` (keep the VM after a failure), `--json` (spindle70 LogLine JSON). Runs the HEAD **commit**, never the dirty tree.71- `bsdkrun ci ls [--event E]` — workflows and whether each matches that trigger.72- `bsdkrun ci serve [--bind H:P]` — accept `sh.tangled.pipeline` records over HTTP and73 run them (a spindle-compatible runner for a server).74- Every SDK can define workflows in code (`workflow("test").on_push("main").step(...)`)75 and `.run()` them — YAML is generated, never hand-written.7677Docker (a Docker engine in a microVM, driven by the host's own `docker` CLI):78- `bsdkrun docker start [--cpus N] [--mem M] [--disk-size 60G] [--mount PATH]` — boot (or79 resume) the engine, serve its API on a host unix socket, and point `docker` at it via a80 `bsdkrun` context. One VM, always named `bsdkrun-docker`; a second `start` resumes it.81- `bsdkrun docker status [--json]` / `bsdkrun docker stop` / `bsdkrun docker rm [-f]`.82- `bsdkrun docker ps [-a] [--json]` — containers. `bsdkrun docker logs <c> [--tail N]`.83- `bsdkrun docker container <start|stop|restart|kill|pause|unpause|rm> <c>...`.84- `bsdkrun docker disk [--size 100G]` — show or grow the image store.85- `bsdkrun docker env` — the `DOCKER_HOST` line, for a shell not using the context.86- `bsdkrun docker shell` — a shell in the engine **VM** (not in a container).87- Published container ports are mirrored onto the host automatically; `$HOME` is shared88 into the VM at the same path so `-v $PWD:/app` resolves.8990Snapshots (a machine's disk state, copy-on-write):91- `bsdkrun snapshot <id> [name] [-d DESC]` — capture a machine's disk state (BSD guests are powered92 off first, and left stopped; Linux guests keep running).93- `bsdkrun snapshots [machine] [--json]` — list snapshots, newest first.94- `bsdkrun snapshot rm <name>...` — delete snapshots and their data.95- `bsdkrun branch <snapshot|machine> [-d] [--name N] [--port H:G]` — boot a NEW machine from a96 snapshot; naming a machine snapshots it first. Prints the new machine's id.97- `bsdkrun restore <id> <snapshot> [-f]` — put a machine back to a snapshot (`-f` stops it first;98 the replaced state is snapshotted first unless `--no-backup`). Left stopped — `start` to run it.99- `bsdkrun rollback <id> [-f]` — restore to the machine's most recent snapshot.100101Flavors (reusable environments):102- `bsdkrun commit <id> <name>` — freeze a machine into a flavor (like `docker commit`).103- `bsdkrun flavors [--json]` — list the catalog + your snapshots.104- `bsdkrun flavor run [-d] <name> [--port H:G] [-v NAME]` — boot a machine from a flavor.105- `bsdkrun flavor add --base <ref> [--nix PKG] [--provision CMD]... <name>` — define a custom flavor.106- `bsdkrun flavor build <name>` — pre-build a flavor's provisioned rootfs into the cache.107- `bsdkrun flavor rm <name>` — remove a snapshot / user flavor.108109Global networks (shared subnet + internal DNS — like docker-compose):110- `bsdkrun network create <name>` — create a network (starts its shared gvproxy).111- `bsdkrun network ls [--json]` — list networks + member counts.112- `bsdkrun network rm [-f] <name>...` — delete network(s).113- `bsdkrun network connect <machine> <network>` — join/switch a machine (applies on next `start`).114- `bsdkrun network disconnect <machine>` — detach back to isolated (applies on next `start`).115- `bsdkrun network sync <network>` — refresh members' `/etc/hosts` so peers resolve by name.116- Join at boot with `--network <name> [--name <member>]` on `linux`/`freebsd`/`netbsd`.117118Remote access (in-guest agent actions):119- `bsdkrun ssh <id> setup|add-key|status [--user U] [--key K]...` — key-based SSH.120- `bsdkrun tailscale <id> setup|status|install|start [--authkey K] [--hostname H]` — tailscale.121- `bsdkrun systemd <id> setup|status|disable` — systemd as PID 1 (Linux; boot on `-v` to persist).122- `bsdkrun agent update <id>` — refresh a stale baked-in guest agent.123124Disks, images, volumes:125- `bsdkrun prune [--all] [--volumes] [--only KIND]... [-f] [--dry-run]` — reclaim disk:126 stopped machines, unused images, orphaned rootfs trees. Asks with a summary first;127 `--only orphan` is the selection that cannot cost anything. `--all` adds the OCI layer128 cache; `--volumes` adds unused volumes.129- `bsdkrun images [--json]` — list downloaded images.130- `bsdkrun image rm <id>... [-f]` — remove dangling images (refused while a machine uses one).131- `bsdkrun volume ls | rm <name>...` — manage persistent volumes.132- `bsdkrun fetch [--os freebsd|netbsd] [--version V]` — download + prepare a BSD image.133- `bsdkrun versions` — list downloadable BSD builds.134- `bsdkrun grow --disk <path> --size <8G>` — enlarge a disk image (guest expands on next boot).135- `bsdkrun probe` — check that libkrun links and a VM context can be created.136137## Common flags on run commands (`linux`/`freebsd`/`netbsd`)138139- `-d, --detach` — run in background, print the id (like `docker run -d`).140- `--name <NAME>` — machine name (also its DNS name on a `--network`).141- `--network <NAME>` — join a global network.142- `--cpus <N>` / `--mem <MiB>` — resources (defaults 1 / 512).143- `--port <HOST:GUEST>` — forward a host TCP port (repeatable), e.g. `--port 2222:22`.144- `-v, --volume <NAME>` — persist the rootfs/disk to a named volume.145- `--attach-disk PATH[:ro]` — attach an extra raw disk image as virtio-blk (repeatable).146- `--no-net` — boot with no NIC (disables the agent → no `exec`/`shell`).147- `--repo <URL>` — clone a git repo into the guest and `cd` into it on shell open.148- Linux only: `--mount HOST:GUEST[:ro]`, `--entrypoint`, `-e K=V`, `--initramfs`, `--kernel(-version)`.149- BSD only: `--version`, `--persist`, `--disk-size 8G`, `--verbose`.150151## Key behaviors to remember152153- **`start` resumes the machine's own storage** (its disk / rootfs), not a fresh base154 image — snapshot and runtime data survive stop/start.155- **`stop` cleanly powers off BSD guests** (`shutdown -p now`) so their UFS is156 consistent; it takes a few seconds. Linux writes go straight to the host FS.157- **NetBSD name resolution** relies on `/etc/hosts` sync (its resolver rejects the158 gvproxy DNS's AAAA NXDOMAIN). Joins auto-sync; use `network sync` to refresh an159 existing network without restarting members.160- **Snapshots of BSD guests** power the guest off first for a clean, bootable image.161- **Linux storage on macOS is case-sensitive.** The first Linux launch automatically creates a162 sparse APFS store for OCI roots and volumes. Inspect or manage it with `bsdkrun store status`,163 `init`, `attach`, `detach`, and `rm`.164- The desktop app mirrors these: Machines, Images, Volumes, Flavors, and Networks165 views, plus per-machine edit (CPU/RAM, network) and network member browsing.166167## Examples168169```sh170# Ephemeral Alpine, run one command171bsdkrun linux alpine -- echo hi172173# Detached Ubuntu with SSH forwarded, then exec into it174id=$(bsdkrun linux -d --name web --port 2222:22 ubuntu:24.04 -- sleep infinity)175bsdkrun exec -t "$id" bash176bsdkrun ssh "$id" setup # installs your ~/.ssh/id_*.pub177178# FreeBSD, persistent, on a named volume179bsdkrun freebsd -d --name bsd -v bsddata --disk-size 8G180181# Two machines on one network, reachable by name182bsdkrun network create devnet183bsdkrun linux -d --network devnet --name db postgres184bsdkrun linux -d --network devnet --name api myapi # api can `ping db`185186# Snapshot a configured machine and reuse it187bsdkrun commit web my-web-env188bsdkrun flavor run -d my-web-env189```