Choose sandbox tier 2

Set up microVM isolation

Configure the supported microVM path for the node platform.

For: cluster operators.

Configure the supported microVM path for the node platform.

microVMs

Experimental. smolvm does not yet keep a guest inside the folder it shares (smolvm issues #151 and #152), so a guest that breaks its own kernel could walk out of /work into whatever the VM's monitor can see. On Linux that is only what tier 1 lets the monitor see (below), which is why a node never runs one outside it.

A microVM boots a tool's container image with a Linux kernel of its own, in a virtual machine per server. A tool that escapes its MCP server is inside that kernel; to reach the node it must break the kernel and then the virtual machine monitor, and on Linux the monitor itself runs in tier 1. It is the strongest isolation OZ0 has, and the slowest to start: about a second and a half per server here, against a fifth of a second for the same image in Docker.

A tool asks for one in its manifest. Its plugin's agents then require isolation.microvm=ready (and the image's architecture), not runtime.oci=ready: a microVM node needs no Docker.

oz0-plugin.yaml
tools:
  - id: acme_fetch
    package: oci:ghcr.io/acme/fetch-mcp@sha256:4f6c0d1b5e8a…
    isolation: microvm                # boots in a VM of its own, never in a container
    container: {memory_mb: 512, cpus: 1}

A node without microVMs leaves the tool out, Docker or not (it runs only in a microVM, which this node does not offer (...)). A tenant's --oci gvisor requirement does not apply to such a tool: it runs in its VM.

NodemicroVMs
Linux, amd64 or arm64, with KVM and tier 1 (bubblewrap)smolvm 1.24.0, inside tier 1
macOS 26 on Apple siliconApple's container 1.5.0 or later
macOS before 26 on Apple siliconnot yet: smolvm's monitor has no tier-1 sandbox on macOS, and the node never runs it without one
Intel Macsno: neither runtime runs on them
Windowsnot yet: smolvm needs the Windows Hypervisor Platform and Developer Mode or an administrator

Set up a Linux node

  1. Give the node's user /dev/kvm: sudo usermod -aG kvm <user> (log in again, or restart the node's service).
  2. Download smolvm 1.24.0 for your architecture from its GitHub releases (smolvm-1.24.0-linux-x86_64.tar.gz or -linux-arm64.tar.gz), check its SHA-256 against the table below (smolvm's releases are not signed), and unpack it somewhere the node's user can read but not write, such as /opt/smolvm.
  3. Put its smolvm on the node's PATH (ln -s /opt/smolvm-1.24.0-linux-x86_64/smolvm /usr/local/bin/smolvm), or name the folder: OZ0_MICROVM=/opt/smolvm-1.24.0-linux-x86_64 oz0-node run.
  4. Restart the node. It checks every file of smolvm it loads against the release it pins, boots one VM inside tier 1, and logs smolvm booted inside tier 1; the edge labels it isolation.microvm=ready, and oz0 node show <node> and the Nodes page have a MicroVMs line. orchestrator-zero-node sandbox vm --probe --guest "$(command -v orchestrator-zero-node)" asks the same without starting the node. The first start takes about ten seconds more (smolvm unpacks its disk templates once), later ones a second and a half.
ReleaseArchiveSHA-256
1.24.0smolvm-1.24.0-linux-x86_64.tar.gzdf4cf6efcac4cff61238b98d30fe589e3ccfc5d57ade3c04401c808a54050b73
1.24.0smolvm-1.24.0-linux-arm64.tar.gzd08c51a10b49737c9b20501a018be62772d29e39ad9aae82f4e0e1c0fe32dcd0

A smolvm the node binary does not pin is refused: smolvm is not a release this node pins, so it does not run it (...). Naming its folder with OZ0_MICROVM runs it anyway, and the node's facts then say pinned: false with a warning. OZ0_MICROVM=off turns microVMs off. A node whose test VM does not boot inside tier 1 (no tier 1, no access to /dev/kvm, a kernel or bubblewrap that refuses it) offers no microVMs and says why in its log and in oz0 node show --json (facts.microvm.reason). OZ0 never downloads or updates smolvm.

How a tool runs in one

orchestrator-zero-node sandbox vm starts each server's VM: smolvm machine create, start, exec with the tool's MCP over its input and output, and delete, whatever ended it: the tool, the end of its input, a cancelled call (which stops a job's server), or the node stopping.

The monitorin tier 1: bubblewrap's namespaces, no capabilities, no nested user namespace, the vmm seccomp filter (tier 1's, with userfaultfd allowed), /dev/kvm its only device; smolvm's own seccomp filter and Landlock rules on top (SMOLVM_SECCOMP and SMOLVM_LANDLOCK set to enforce)
Networkno network device at all (smolvm's --net is never used). With the plugin's egress, the node's egress proxy's socket is mapped into the VM and the node binary's bridge listens on its loopback, so the tenant's and the plugin's hosts are reachable and nothing else
Filesthe job's work folder at /work, the one folder the VM shares (an installed plugin's server shares a folder of the runtime's own); with egress also the node binary, read-only, at /oz0. The VM's own disk is its own, and goes with it. A file a call is given is placed under /work/.oz0-files/ and the call's _meta["oz0/files"] names it there, so the tool opens it inside the VM
Userthe node's uid and gid inside the VM, HOME=/tmp
Secretsthe plugin's settings and secrets by name: smolvm reads each value from its own environment, so none is on a command line, and no process outside the VM holds one under its own name (each passes through as OZ0_VMENV_<name>). A microVM tool may not name a variable PATH, HOME, TMPDIR, a proxy's (HTTPS_PROXY and the like), or LD_*, DYLD_*, SMOLVM_* or OZ0_VMENV_*
Limitsthe tool's memory (--mem) and CPUs, rounded up to whole vCPUs; the monitor's sandbox gets the node's tier-1 limits on top
Dataeach VM's smolvm data folder is its own, so no VM can touch another's disks. It lives under the temporary directory (oz0-vm-<id>-<random>, because smolvm's agent socket inside it must have a short path), named by the VM's record in <data dir>/microvm/vms/, and goes with the VM

macOS 26

Install Apple's container 1.5.0 or later (its signed installer, from its GitHub releases), and run container system start in the node's user session.

The node finds container on PATH, checks its version and that its services answer, and runs each tool with container run --rm --network none --read-only --cap-drop ALL, the node's user, whole CPUs, --ulimit nproc (

Apple's runtime has no process limit), and with egress the egress proxy's socket file and the node binary.

Apple's runtime has no no-new-privileges, and its VMs are started by its own launchd services, so they are not inside tier 1: their kernel is the boundary.

The CLI starts with a clean environment (CVE-2026-64786), and the tool's variables reach it only in an env file only the node's user may read, removed when the tool stops (a value with a line break is refused).

A Mac that has Apple's container fetches the Linux node binary the VM's bridge needs from its edge, by the release's signed manifest, of its own version only, after it found the runtime (a Mac without it, an Intel Mac and a Windows node never fetch it); a node built without a release key has no microVMs.

Older versions' copies under <data dir>/microvm/guest/ are removed at the node's start.

Neither macOS path has run on a Mac yet: scripts/test-macos-microvm.sh checks one.

What is tested

make e2e-oci-tools runs a microVM node where /dev/kvm exists. A node without Docker, in a container given /dev/kvm, finds smolvm on its PATH, checks its pins and boots it inside tier 1. The plugin's microVM tools run there alone; a node with Docker leaves them out.

The check verifies that the guest has its own kernel and no network without egress. Its only write visible to the host is in its work folder. An allowed host is reachable through the proxy's socket and another is refused. The monitor shows tier 1's seccomp filter and smolvm's, no capabilities and no new privileges. Stopping the node takes its VMs with it. OZ0_E2E_MICROVM=0 skips the check.

Earlier section link

Copyright © 2026