Choose sandbox tier 2

Configure remote sandboxes

Review remote sandbox egress and lifetime limits before enabling a service.

For: cluster operators.

Review remote sandbox egress and lifetime limits before enabling a service.

Remote sandboxes

Some nodes have no local tier 2: Windows nodes, Intel Macs, hosts with rootless containers only, VMs without KVM. A tool with isolation: remote runs in a remote sandbox instead: a sandbox of an E2B-compatible service, which runs each one as a microVM with a kernel of its own, far from the node. One plugin, ai.oz0/sandbox-e2b, reaches E2B Cloud, a self-hosted E2B and CubeSandbox.

Remote sandboxes have automated coverage against an API stand-in. E2B Cloud and self-hosted E2B have not been validated; see integration maturity.

A remote sandbox is not a local one. Know what changes before you allow it:

A container on the nodeA remote sandbox
Where the code and data areOn the nodeAt the service: the tool's image is a template there, and every call's arguments and results go there
FilesThe tool's own containerNo mount of the node's: files go in and out through the protocol, at most 4 MiB each; calls that hand the tool files are refused for now
NetworkNone, or the node's egress proxy with the tenant's and the plugin's listThe service's own rules, which are weaker (below)
SecretsBy name, from the tenantNone: a plugin that declares secrets or connections runs no tool remotely
Who decidesThe plugin's installThe node's operator configures the service; the tenant's admins allow it

Set up a node

The node's operator configures the service, never a tenant. Install the plugin on the node (it needs Python 3.13 and nothing else):

uv tool install ./plugins/sandbox-e2b    # from a checkout of the repository

Put the service's API key in a file in the node's data directory (here the default on Linux, --data-dir changes it), which no sandboxed process can read, readable by the node's user only:

mkdir -p ~/.local/share/orchestrator-zero-node/secrets && install -m 600 /dev/null ~/.local/share/orchestrator-zero-node/secrets/e2b-key
$EDITOR ~/.local/share/orchestrator-zero-node/secrets/e2b-key

Build a template of the tool's image at the service, from the image pinned by digest (for E2B, a template whose Dockerfile is FROM <image>@sha256:...), and map the image to it:

{"ghcr.io/acme/fetch@sha256:4c2a...": "acme-fetch"}

Then start the node with the backend:

orchestrator-zero-node run --remote-sandbox \
  "e2b=oz0-sandbox-e2b --api-key-file $HOME/.local/share/orchestrator-zero-node/secrets/e2b-key --templates /etc/oz0/e2b-templates.json"

For a self-hosted E2B or CubeSandbox, add --domain, --api-url (and --sandbox-url when one address routes to every sandbox) and --ca-file for a private CA. oz0-sandbox-e2b doctor with the same options checks the key. The node checks the backend when it starts, destroys the sandboxes it left behind, and reports it; oz0 node show <node> says:

Remote sandbox     e2b (e2b 0.1.0), reaching api.e2b.app

Allow it for a tenant

Remote sandboxes are off for every tenant until one of its admins allows them, because the tools' code and data leave the node:

oz0 tenant remote-sandbox default allow

The audit log keeps each change (sandbox.remote_allowed, sandbox.remote_denied). oz0 tenant remote-sandbox default deny stops them: the tenant's nodes restart their runtimes, and every remote sandbox they had is destroyed.

A remote sandbox does not meet a tenant's gVisor requirement: in a job an outside event started, a tenant that requires gVisor gets a refusal for a remote tool's call, which says why, never the remote sandbox in gVisor's place. The service's egress rules are weaker than the node's proxy (below), and nothing of the node's sees the tool's traffic. Jobs a person started still run the tool remotely.

In a plugin

tools:
  - id: fetch
    package: oci:ghcr.io/acme/fetch@sha256:4c2a...
    isolation: remote

plugin lint refuses isolation: remote for a tool that is not from an OCI image, and in a plugin that declares requires.secrets, requires.optional_secrets or a connection that hands the tool a token: no secret leaves the node. The agents that call the tool require isolation.remote=ready, which only nodes with a backend that answered get, and only for tenants that allowed remote sandboxes. Each job's trace says which steps ran remotely, in oz0 job trace and, as "Ran in: a remote sandbox of e2b", in the console's step inspector:

+380ms  31ms  tool   fetch.get   node-5fb2c18ef4d5 (remote sandbox: e2b)   ok

Egress is the service's, and weaker

The node never sends the service more than its own list allows; what the service cannot hold as the node would, it leaves out:

The node's entryIn the remote sandbox
No entriesNo network at all
api.example.com, *.example.comAllowed by name on ports 80 and 443, as on the node
api.example.com:443Allowed by name on both 80 and 443
db.example.com:5432, host:*, host:22/tcpLeft out: E2B filters names only on ports 80 and 443
An IP addressLeft out: E2B would allow every port of it
!uploads.example.com inside an allowed *.example.comRefused: E2B's denies take addresses only

Two things are weaker than the node's egress proxy, and you cannot change them: names are filtered only on ports 80 (the Host header) and 443 (the TLS server name), and when any name is allowed E2B allows its DNS resolver, 8.8.8.8, too, so DNS queries, and anything tunnelled through them, are not filtered. The plugin never adds a resolver of its own.

Do not treat these rules as an allowlist. A filter on the Host header and the TLS server name judges what the client itself says, and the sandbox has a resolver and its own route out, with no proxy of the node's in between: what a client names and where its traffic goes can differ, and DNS is open besides.

Assume a tool in a remote sandbox can reach hosts its list leaves out, and send it nothing it could not be allowed to pass on: no secret leaves the node, but call arguments and results do.

Remote sandboxes have still not run against the real service, so nothing here has been measured against E2B itself (integration maturity). A tenant that needs the node's rules keeps such tools on nodes with a local tier 2.

When sandboxes end

The node destroys a remote sandbox when its tool's server stops, when its runtime restarts and when the backend breaks the protocol. A cancelled call of a job started from outside (a webhook, a schedule) stops that job's server, so it destroys the server's sandbox too; a long-lived server keeps its sandbox through a cancelled call, as a local tool's server keeps its container, and the sandbox ends when the server or the runtime stops.

If the node itself dies, the sandbox ends after its timeout (10 minutes after the node was last heard from), and the node destroys what it left when it starts again. Every remote sandbox the node made carries the label oz0.node=<node ID>.

Earlier section link

Copyright © 2026