Skip to content

Run in a VM

runtime: vm runs an agent in its own smolvm microVM. It’s for an agent you leave alone with code nobody has reviewed: a smoke tester that installs and runs a fresh build, or an agent that runs whatever a pull request contains. Whatever that code does, it does inside the VM, not to your machine.

A container shares your kernel and usually your harness login. A VM has its own kernel, and starts with nothing from your host except the agent’s workdir, read-only.

Set it up

Install smolvm:

Terminal window
brew install smol-machines/tap/smolvm

Then give the agent runtime: vm and a vm: block. The rest of the workforce keeps its own runtime:

runtime: local
agents:
smoke:
runtime: vm
acp: { preset: pi }
vm:
image: ghcr.io/zooid-ai/agent-pi:latest # defaults to the preset's image
cpus: 2
memory: 4GiB
disk: 16GiB
allow_hosts: [github.com, registry.npmjs.org]

vm.image is a registry reference, or a local docker save archive or rootfs directory when it starts with /, ./, ../ or ~/.

The daemon creates and starts the machine when it starts, and stops it (keeping its disk) when it stops. Changing image, cpus, memory, disk, allow_hosts or mounts, or rebuilding a local image, recreates the machine and discards its disk. Changing vm.env doesn’t.

What the VM gets

Workdirthe agent’s workdir, at /workspace, read-only. Not even root in the VM can write it.
Networknone, unless you list hosts in vm.allow_hosts. Then exactly those hosts, nothing else. Your host isn’t reachable.
Envonly what you list in vm.env, with the same rules as container.env.
Mountsonly what you list in vm.mounts, with the same entries as container.mounts. Directories only; rw unless you set mode: ro.
Credentialsnone of yours. Zooid passes no login or key of its own.
Zooid MCPnot yet: a vm agent has no zooid_* tools in this release.

The daemon refuses to start if a vm agent’s workdir holds .env*, .dev.vars* or .claude/settings.local.json, since the VM can read everything in it.

Giving it a login

An agent needs a model credential. Pass one the same way you would to a container: a key in vm.env, or a login directory in vm.mounts. A login that refreshes its own token needs a rw mount so the refreshed token reaches the next run:

vm:
allow_hosts: [chatgpt.com, auth.openai.com, github.com]
env:
PI_CODING_AGENT_DIR: /pi
mounts:
- { host: ~/.zooid/smoke-pi, target: /pi, create: true }

Log the harness in once with that directory as its config dir, and the agent uses it from then on.