← Docs
Several agents, laptops or CI jobs can share a set of VMs without stepping on each other. Pool VMs are handed out with exclusive, expiring leases. When a lease ends, the VM is reset to its clean image, and all work from that lease is gone, including its keys.
Create VMs as usual, set them up the way you want (repos, dependencies, logins), then add them to the pool:
boxery new pool-1 && boxery new pool-2
# …install whatever a fresh lease should start with…
boxery pool add pool-1
boxery pool add pool-2
boxery pool lspool add locks the VM down so that only lease holders can reach it. It ends existing ssh sessions, wipes pushed grants, and captures the NAME@boxery-clean image that each reset returns to. Works on every backend with pools (all but OpenShell), including Lima vz. On Lima the capture stops a running VM and starts it again, so whatever it was running ends.
To take a VM out of the pool after its lease ends, run boxery pool rm NAME. It becomes a normal VM again. --force removes it even while it's leased.
From pi, which handles everything for you:
pi --vm pool # lease, renew while pi runs, release on exit
pi --vm pool --vm-ttl 3600
/vm pool # inside a running pi; /vm off releasesFrom an agent over MCP (or pi), with the vm_lease tool: {"action":"acquire"} leases a VM and makes it current, and the lease is renewed while the session is in use and released when it ends. See Pool leases.
From the CLI or scripts:
read NAME LEASE < <(boxery lease acquire --ttl 1800 --wait 60)
boxery run "$NAME" make test # runs in ~/project
boxery lease renew "$NAME" # extend
boxery lease release "$NAME" # give it back; the VM is reset
boxery lease ls # what you holdacquire, renew, release and leases still work as the older spellings. Borrowing is the lease family; adding VMs to the pool (boxery pool add|rm|ls) is administration.
lease acquire exits with status 4 if no VM is free before --wait runs out. If you give up first (Ctrl-C), the host notices when it can't hand over a lease it got for you, gives that VM back and notes it in lease-orphans.log in its config directory. That needs the connection to close cleanly: if it just drops (a laptop going to sleep), sshd can hold it open for a long time, and the lease's TTL is what frees the VM.
Pool VMs can also run GitHub Actions jobs, each in a freshly reset VM: the host's runner manager holds a lease per runner (holder github-runner) on the VMs you give it, and boxery runners once holds one for a single job (holder github-runner-local). See GitHub Actions runners.
- Exclusive. A pool VM accepts only the host's key and the current lease's key. Each lease gets a fresh ssh keypair, stored in
~/.boxery/leases/. - Expiring. TTLs run from 1 minute to 8 hours (default 30 min). The pi extension renews at a third of the TTL. A reaper on the host (systemd timer or launchd agent, every minute) resets expired leases. A sleeping laptop or a crashed agent never strands a VM.
- Fenced. Release or expiry resets the VM to its clean image, which also removes the lease key. Forking a leased VM doesn't carry the lease key over. A holder whose lease is gone is locked out, even with a connection that was already open.
- A proxy token per lease. Each lease gets its own LLM proxy token, and its
http:grants are bound to that lease. When the lease ends, the token stops working, so a copy taken from inside the VM can't spend through the proxy or use the next holder's grants. - Reboot-safe. Leases are tied to the host's boot ID. After a host reboot, every lease is void and gets reset.
- Crash-safe. State lives in SQLite (
~/.config/boxery/leases.db) as a small state machine with generation fencing. A failed acquire or reset never leaves a VM half-assigned. Such VMs are markedbrokeninstead, andpool lsshows them.
free ──acquire──▶ acquiring ──▶ leased ──release / expiry / reboot──▶ resetting ──▶ free
└──fail──▶ broken
pool ls and ls (its STATUS column: pool free, leased by HOLDER, 2h left, …; lease.state in --json) show these states.
To recover a broken VM, fix it, then run boxery pool rm NAME && boxery pool add NAME.
Design notes: ARCHITECTURE.md → Leases. Not built yet (gateway, --keep, profiles): plan.