Start with one VM
Install skiff, start a Linux VM on your Mac, reach it over SSH and bring its ports to localhost.
Install
skiff needs Apple Silicon, macOS 26 or newer (what Apple's container supports), Homebrew and Go 1.26+. Check a Mac with skiff doctor.
prepare is a one-time setup of about a minute and safe to run again. It installs Apple's container through Homebrew if needed, starts its service, builds the guest image (Ubuntu 24.04 with systemd, sshd, your key and an internet-only firewall) and writes an SSH key pair to ~/.skiff.
Create and connect
dev is running at 192.168.64.2 skiff ssh dev ssh dev.skiff
new boots a fresh Ubuntu 24.04 in about half a second and returns once it answers SSH, about two seconds after you hit enter. Without a name, VMs are called vm1, vm2 and so on.
You are root in an Ubuntu 24.04 with its own kernel, disk and IP. To use plain ssh, scp, rsync or your IDE, add one line to ~/.ssh/config:
Then ssh dev.skiff works from anywhere. skiff's ProxyCommand connects to the VM and verifies the host key pinned at creation, so neither port forwarding nor sudo is needed on macOS.
Size
Defaults are 2 vCPUs and 1 GiB RAM, the minimum Apple's container accepts. The disk is a sparse volume that grows as the VM writes, so there is nothing to size there.
The guest is a plain Ubuntu 24.04 with git, curl and jq. Install what a task needs over SSH; the agent guide shows the commands for Node, Claude Code and gh.
Forward ports
Guest ports appear as localhost:PORT on the Mac. Forwards survive stop and start and go away with the VM. Host ports must be 1024 or higher.
skiff forward web without ports lists them; skiff unforward web 5432 removes one.
What a VM can reach
The internet, through NAT, and nothing else: a firewall in the guest rejects your Mac, your LAN and other VMs. Root in the VM can lift it, so treat it as a guard rail. The only controlled way back to the Mac is the capability broker.
Command reference
Every command has --help with all flags and examples.
| Command | What it does |
|---|---|
doctor | Explain what this Mac is missing. |
prepare | Set up or update Apple's container runtime and the guest image. |
new [NAME] [--vcpus N --mem MiB -p PORT --grant CAP --owner O --slot S] | Create and boot a VM. |
new … -p [HOST:]GUEST --grant CAP | Forward ports and grant capabilities right away. |
new … --owner NAME --slot N | Label a VM a tool manages, for gc. |
ls [--json] | Every VM with state, IP, size, ports, grants, owner, age and SSH destination. |
inspect NAME [--json] | Details of one VM. |
ssh NAME [command…] | Root shell, or a command line run by the guest shell. Carries the broker. |
exec NAME -- cmd [arg…] | Run a command with its argv passed verbatim. Carries the broker. |
tunnel NAME | Hold only the broker forward, for editors and agent runners. |
stop NAME / start NAME | Shut down keeping the disk, boot again. |
rm NAME | Stop and delete, including grants and token on the Mac. |
gc [--owner O] [--older-than 7d] [--keep VM] [--dry-run] | Remove owned VMs a tool lost track of, and leftover host state. |
forward NAME [[HOST:]GUEST…] / unforward NAME PORT… | List, add or remove port forwards. |
grant / revoke NAME CAP… | Allow or withdraw capabilities. See Grants. |
grants NAME | Show what a VM may use. |
cap ls / show / init / lint / add | Manage capability files. See Reference. |
secret set / rm NAME | Store or delete a credential in the macOS Keychain. |
Troubleshooting
- Exit code 255 from
sshorexecmeans the connection failed; checkskiff ls. - After a reboot of the Mac, the next
skiffcommand starts the container service and the VMs that were running come back; ones stopped withskiff stopstay stopped. If one fails to boot,skiff start NAMEretries it. brokeninskiff lsmeans damaged metadata;skiff rm NAMEcleans it up.- Boot log of a VM:
container machine logs NAME; port forwards:~/.skiff/relay.log - Reset everything:
skiff rmeach VM, thenrm -rf ~/.skiff
Before you leave
Stop keeps the disk; rm is a separate step.