CLI Commands

Sandbox Commands

Lifecycle

heyvm create --name demo --image default   # create a sandbox (--name is required)
heyvm create --name demo --backend firecracker --image default
heyvm start <id>                        # start a stopped sandbox
heyvm stop <id>                         # stop it
heyvm restart <id>
heyvm get <id>                          # inspect one
heyvm rm <id>                           # delete (aliases: delete, destroy)
Command Purpose
create Create a sandbox (--name required; --image, --backend — alias --backend-type — …)
start / stop / restart Lifecycle control
get Inspect a single sandbox
rm Delete (aliases delete, destroy)
fork Fork a sandbox into a copy
list / list-inactive List sandboxes
prune Remove stopped sandboxes
checkpoint / snapshot Capture state
resize Change CPU / memory / disk
edit-ttl Change time-to-live
edit-start-command Change the persistent start command
update Replace a deployed sandbox's mount contents from an archive
wait-for Block until a sandbox reaches a state
backends List available backends on this host
wt Create a git-worktree sandbox and open a shell in it

rm prompts for confirmation and refuses to prompt when stdin is not a TTY — always pass -y (--yes) from scripts and CI.

Backend support for the duplication commands varies, and is not uniform across platforms:

Command Supported on
snapshot apple_virt, and the Linux backends. On apple_container it needs Apple's container commit/container import, which many container builds omit.
checkpoint / restore Linux only (libvirt, firecracker, kvm). Not available on either macOS backend.
fork Linux only, and requires a sandbox created with --project-snapshot.
edit-start-command Saves the new command but does not restart the sandbox — run heyvm restart <id> for it to take effect.

--project-snapshot is honored only by the bubblewrap backend; on any other backend it is accepted and ignored. See macOS Capabilities for the full macOS matrix.

Exposing ports

--open-port maps a guest port to a host port when the sandbox is created. The argument is HOST:GUEST, or a single value to use the same port on both sides:

heyvm create --name api --open-port 18734:5000   # host 18734 -> guest 5000
heyvm create --name api --open-port 8080         # host 8080  -> guest 8080

The mapping is fixed at creation time — it cannot be added to a running sandbox. To expose a port afterwards, use heyvm port-forward on a backend with SSH. Note that apple_virt ignores --open-port: its guests get a host-routable NAT IP and are reached directly. See Networking & Sharing.

Running code

heyvm exec <id> -- <command>     # run a command, capture output
heyvm sh <id>                    # interactive shell
heyvm run-host -- <command>      # run on the host
heyvm logs <id>                  # view logs

# Run an isolated code snippet (experimental, hyperlight-based — no sandbox id)
heyvm run --lang js --code 'console.log(1 + 1)'
heyvm run --lang python --file script.py
Command Purpose
exec Execute a command inside a sandbox
sh Open an interactive shell
run-host Run a command on the host machine
logs Show sandbox logs
run Execute an isolated JS/Python snippet with the experimental hyperlight runner (--lang, --code/--file) — not tied to a sandbox

exec semantics

Everything after -- is the command. exec does not run a shell for you, so pipes, redirects, and compound commands need an explicit wrapper:

heyvm exec <id> -- ls -la
heyvm exec <id> -- sh -c 'ls -la | wc -l'        # pipes need sh -c
heyvm exec <id> --env KEY=value -- printenv KEY
heyvm exec <id> --cwd /srv --user root -- whoami

exec does not forward stdin. Piping into it (echo … | heyvm exec <id> -- bash -s) sends nothing to the guest. To run a local script remotely, pass it as an environment variable and decode it guest-side:

B64=$(base64 < script.sh)
heyvm exec <id> --env B64="$B64" -- sh -c 'echo "$B64" | base64 -d | bash'

Background processes survive the call that launched them, so a long-running service can be started with exec and left running:

heyvm exec <id> -- bash -c 'nohup my-server & disown'

Use heyvm sh <id> when you need a real interactive terminal.

Transferring VMs

heyvm transfer moves a VM to another machine over P2P: disks, config, and (where the backend supports it) the memory snapshot travel directly to the destination daemon, and the source sandbox is destroyed once the destination verifies the restore.

# Move a VM to one of your registered daemons (by name or id),
# or directly to a daemon's heyo:// connection ticket
heyvm transfer demo --to office-mac
heyvm transfer demo --to heyo://…

# Copy instead of move — the source keeps running
heyvm transfer demo --to office-mac --keep-source

# No cloud account? Serve the bundle and pull it from the other machine
heyvm transfer demo --serve          # prints a sync ticket
heyvm sync pull <ticket>             # run this on the destination

# Skip the memory snapshot — or insist on it
heyvm transfer demo --to office-mac --disk-only
heyvm transfer demo --to office-mac --require-memory

# Rename on arrival and leave it stopped
heyvm transfer demo --to office-mac --name demo-2 --no-start
Flag Purpose
--to <daemon> Destination: the name or id of a daemon registered to your account, or a daemon's heyo:// connection ticket
--serve No-cloud fallback: package the VM, serve it over P2P, and print a ticket for heyvm sync pull on the destination
--keep-source Keep the source sandbox running (copy instead of move)
--disk-only Transfer disk + config only; skip the memory snapshot
--require-memory Fail (and roll back the destination) if the memory snapshot can't be restored there, instead of degrading to disk-only
--name <name> Name for the sandbox on the destination (defaults to the source name)
--no-start Don't start the restored VM on the destination
--timeout-secs <n> How long to wait for the destination to pull + restore before resuming the source (default 1800)
-r, --relay <url> Relay URL for short-code ticket registration/resolution

Memory snapshots travel on the Firecracker backend; KVM and macOS (Apple Virtualization) transfers carry disk + config only. If the memory snapshot can't be restored on the destination, the transfer degrades to disk-only — pass --require-memory to make that a hard failure instead.