agents.defaults.sandbox (global) or agents.list[].sandbox (per-agent). The Gateway process always stays on the host; only tool execution moves into the sandbox when enabled.
This is not a perfect security boundary, but it materially limits filesystem and process access when the model does something dumb.
What gets sandboxed
- Tool execution:
exec,read,write,edit,apply_patch,process, etc. - The optional sandboxed browser (
agents.defaults.sandbox.browser).
- The Gateway process itself.
- Any tool explicitly allowed to run outside the sandbox via
tools.elevated. Elevated exec bypasses sandboxing and runs on the configured escape path (gatewayby default, ornodewhen the exec target isnode). If sandboxing is off,tools.elevatedchanges nothing since exec already runs on the host. See Elevated Mode.
Modes, scope, and backend
Three independent settings control sandbox behavior:| Setting | Key | Values | Default |
|---|---|---|---|
| Mode | agents.defaults.sandbox.mode | off, non-main, all | off |
| Scope | agents.defaults.sandbox.scope | agent, session, shared | agent |
| Backend | agents.defaults.sandbox.backend | docker, ssh, openshell | docker |
off: no sandboxing.non-main: sandbox every session except the agent’s main session. The main session key is alwaysagent:<agentId>:main(orglobalwhensession.scopeis"global"); it is not configurable. Group/channel sessions use their own keys, so they always count as non-main and get sandboxed.all: every session runs in a sandbox.
agent: one container per agent.session: one container per session.shared: one container shared by all sandboxed sessions (per-agentdocker/ssh/browseroverrides are ignored under this scope).
agents.defaults.sandbox.ssh; OpenShell-specific config lives under plugins.entries.openshell.config.
| Docker | SSH | OpenShell | |
|---|---|---|---|
| Where it runs | Local container | Any SSH-accessible host | OpenShell managed sandbox |
| Setup | scripts/sandbox-setup.sh | SSH key + target host | OpenShell plugin enabled |
| Workspace model | Bind-mount or copy | Remote-canonical (seed once) | mirror or remote |
| Network control | docker.network (default: none) | Depends on remote host | Depends on OpenShell |
| Browser sandbox | Supported | Not supported | Not supported yet |
| Bind mounts | docker.binds | N/A | N/A |
| Best for | Local dev, full isolation | Offloading to a remote machine | Managed remote sandboxes with optional two-way sync |
Docker backend
Docker is the default backend once sandboxing is enabled. It runs tools and sandbox browsers locally through the Docker daemon socket (/var/run/docker.sock); isolation comes from Docker namespaces.
Defaults: network: "none" (no egress), readOnlyRoot: true, capDrop: ["ALL"], image openclaw-sandbox:bookworm-slim.
To expose host GPUs, set agents.defaults.sandbox.docker.gpus (or the per-agent override) to a value like "all" or "device=GPU-uuid". This is passed to Docker’s --gpus flag and requires a compatible host runtime such as NVIDIA Container Toolkit.
Sandboxed browser
- The sandbox browser auto-starts (ensures CDP is reachable) when the browser tool needs it. Configure via
agents.defaults.sandbox.browser.autoStart(defaulttrue) andautoStartTimeoutMs(default 12s). - Sandbox browser containers use a dedicated Docker network (
openclaw-sandbox-browser) instead of the globalbridgenetwork. Configure withagents.defaults.sandbox.browser.network. agents.defaults.sandbox.browser.cdpSourceRangerestricts container-edge CDP ingress with a CIDR allowlist (for example172.21.0.1/32).- noVNC observer access is password-protected by default; OpenClaw emits a short-lived token URL that serves a local bootstrap page and opens noVNC with the password in the URL fragment (not query string or header logs).
agents.defaults.sandbox.browser.allowHostControl(defaultfalse) lets sandboxed sessions target the host browser explicitly.- Optional allowlists gate
target: "custom":allowedControlUrls,allowedControlHosts,allowedControlPorts.
SSH backend
Usebackend: "ssh" to sandbox exec, file tools, and media reads on an arbitrary SSH-accessible machine.
command: "ssh", workspaceRoot: "/tmp/openclaw-sandboxes", strictHostKeyChecking: true, updateHostKeys: true.
- Lifecycle: OpenClaw creates a per-scope remote root under
sandbox.ssh.workspaceRoot. On first use after create or recreate, it seeds that remote workspace from the local workspace once. After that,exec,read,write,edit,apply_patch, prompt media reads, and inbound media staging run directly against the remote workspace over SSH. OpenClaw does not sync remote changes back to the local workspace automatically. - Authentication material:
identityFile/certificateFile/knownHostsFilereference existing local files.identityData/certificateData/knownHostsDataaccept inline strings or SecretRefs, resolved through the normal secrets runtime snapshot, written to temp files with mode0600, and deleted when the SSH session ends. If both a*Fileand*Datavariant are set for the same item,*Datawins for that session. - Remote-canonical consequences: the remote SSH workspace becomes the real sandbox state after the initial seed. Host-local edits made outside OpenClaw after the seed step are not visible remotely until you recreate the sandbox.
openclaw sandbox recreatedeletes the per-scope remote root and seeds again from local on next use. Browser sandboxing is not supported on this backend, andsandbox.docker.*settings do not apply to it.
OpenShell backend
Usebackend: "openshell" to sandbox tools in an OpenShell-managed remote environment. OpenShell reuses the same SSH transport and remote filesystem bridge as the generic SSH backend, and adds OpenShell lifecycle (sandbox create/get/delete/ssh-config) plus an optional mirror workspace sync mode.
mode: "mirror" (default) keeps the local workspace canonical: OpenClaw syncs local into the sandbox before exec and syncs back after. mode: "remote" seeds the remote workspace once from local, then runs exec/read/write/edit/apply_patch directly against the remote workspace without syncing back; local edits after the seed are invisible until you openclaw sandbox recreate. Under scope: "agent" or scope: "shared", that remote workspace is shared at the same scope. Current limitations: sandbox browser isn’t supported yet, and sandbox.docker.binds doesn’t apply to this backend.
openclaw sandbox list/recreate/prune all treat OpenShell runtimes the same as Docker runtimes; prune logic is backend-aware.
For the full prerequisites, configuration reference, workspace-mode comparison, and lifecycle details, see OpenShell.
Workspace access
agents.defaults.sandbox.workspaceAccess controls what the sandbox can see:
| Value | Behavior |
|---|---|
none (default) | Tools see an isolated sandbox workspace under ~/.openclaw/sandboxes. |
ro | Mounts the agent workspace read-only at /agent (disables write/edit/apply_patch). |
rw | Mounts the agent workspace read/write at /workspace. |
mirror mode still uses the local workspace as the canonical source between exec turns, remote mode uses the remote OpenShell workspace as canonical after the initial seed, and workspaceAccess: "ro"/"none" still restrict write behavior the same way.
Inbound media is copied into the active sandbox workspace (media/inbound/*).
Skills: the
read tool is sandbox-rooted. With workspaceAccess: "none", OpenClaw mirrors eligible skills into the sandbox workspace (.../skills) so they can be read. With "rw", workspace skills are readable from /workspace/skills, and eligible managed, bundled, or plugin skills are materialized into the generated read-only path /workspace/.openclaw/sandbox-skills/skills.Custom bind mounts
agents.defaults.sandbox.docker.binds mounts additional host directories into the container. Format: host:container:mode (e.g., "/home/user/source:/source:rw").
Global and per-agent binds are merged (not replaced). Under scope: "shared", per-agent binds are ignored.
agents.defaults.sandbox.browser.binds mounts additional host directories into the sandbox browser container only. When set (including []), it replaces docker.binds for the browser container; when omitted, the browser container falls back to docker.binds.
Images and setup
Default Docker image:openclaw-sandbox:bookworm-slim
Source checkout vs npm installThe
scripts/sandbox-setup.sh, scripts/sandbox-common-setup.sh, and scripts/sandbox-browser-setup.sh helper scripts are only available when running from a source checkout. They are not included in the npm package.If you installed OpenClaw via npm install -g openclaw, use the inline docker build commands shown below instead.Build the default image
From a source checkout:From an npm install (no source checkout needed):The default image does not include Node. If a skill needs Node (or other runtimes), either bake a custom image or install via
sandbox.docker.setupCommand (requires network egress + writable root + root user).OpenClaw does not silently substitute plain debian:bookworm-slim when openclaw-sandbox:bookworm-slim is missing. Sandbox runs that target the default image fail fast with a build instruction until you build it, because the bundled image carries python3 for the sandbox write/edit helpers.Optional: build the common image
For a more functional sandbox image with common tooling (for example From an npm install, build the default image first (see above), then build the common image on top using
curl, jq, Node 24, pnpm, python3, and git):From a source checkout:scripts/docker/sandbox/Dockerfile.common from the repository.Then set agents.defaults.sandbox.docker.image to openclaw-sandbox-common:bookworm-slim.Optional: build the sandbox browser image
From a source checkout:From an npm install, build using
scripts/docker/sandbox/Dockerfile.browser from the repository.agents.defaults.sandbox.docker.network.
Sandbox browser Chromium defaults
Sandbox browser Chromium defaults
The bundled sandbox browser image applies conservative Chromium startup flags for containerized workloads:
--remote-debugging-address=127.0.0.1--remote-debugging-port=<derived from OPENCLAW_BROWSER_CDP_PORT>--user-data-dir=${HOME}/.chrome--no-first-run--no-default-browser-check--disable-dev-shm-usage--disable-background-networking--disable-breakpad--disable-crash-reporter--no-zygote--metrics-recording-only--password-store=basic--use-mock-keychain--headless=newwhenbrowser.headlessis enabled.--no-sandbox --disable-setuid-sandboxwhenbrowser.noSandboxis enabled.--disable-3d-apis,--disable-gpu,--disable-software-rasterizerby default; these graphics-hardening flags help containers without GPU support. SetOPENCLAW_BROWSER_DISABLE_GRAPHICS_FLAGS=0if your workload needs WebGL or other 3D features.--disable-extensionsby default; setOPENCLAW_BROWSER_DISABLE_EXTENSIONS=0for extension-reliant flows.--renderer-process-limit=2by default; controlled byOPENCLAW_BROWSER_RENDERER_PROCESS_LIMIT=<N>, where0keeps Chromium’s default.
browser.extraArgs to append additional startup flags.Network security defaults
Network security defaults
network: "host"is blocked.network: "container:<id>"is blocked by default (namespace join bypass risk).- Break-glass override:
agents.defaults.sandbox.docker.dangerouslyAllowContainerNamespaceJoin: true.
scripts/docker/setup.sh can bootstrap sandbox config. Set OPENCLAW_SANDBOX=1 (or true/yes/on) to enable that path. Override the socket location with OPENCLAW_DOCKER_SOCKET. Full setup and env reference: Docker.
setupCommand (one-time container setup)
setupCommand runs once after the sandbox container is created (not on every run). It executes inside the container via sh -lc.
Paths:
- Global:
agents.defaults.sandbox.docker.setupCommand - Per-agent:
agents.list[].sandbox.docker.setupCommand
Common pitfalls
Common pitfalls
- Default
docker.networkis"none"(no egress), so package installs will fail. docker.network: "container:<id>"requiresdangerouslyAllowContainerNamespaceJoin: trueand is break-glass only.readOnlyRoot: trueprevents writes; setreadOnlyRoot: falseor bake a custom image.usermust be root for package installs (omituseror setuser: "0:0").- Sandbox exec does not inherit host
process.env. Useagents.defaults.sandbox.docker.env(or a custom image) for skill API keys. - Values in
agents.defaults.sandbox.docker.envare passed as explicit Docker container environment variables. Anyone with Docker daemon access can inspect them with Docker metadata commands such asdocker inspect. Use a custom image, mounted secret file, or another secret delivery path if that metadata exposure is not acceptable.
Tool policy and escape hatches
Tool allow/deny policies still apply before sandbox rules. If a tool is denied globally or per-agent, sandboxing doesn’t bring it back.tools.elevated is an explicit escape hatch that runs exec outside the sandbox (gateway by default, or node when the exec target is node). /exec directives only apply for authorized senders and persist per session; to hard-disable exec, use tool policy deny (see Sandbox vs Tool Policy vs Elevated).
Debugging:
openclaw sandbox listshows sandbox containers, status, image match, age, idle time, and associated session/agent.openclaw sandbox explain [--session <key>] [--agent <id>]inspects effective sandbox mode, host workspace, runtime workdir, Docker mounts, tool policy, and fix-it config keys. ItsworkspaceRootfield remains the configured sandbox root;effectiveHostWorkspaceRootshows where the active workspace actually lives.openclaw sandbox recreate [--all | --session <key> | --agent <id>] [--browser] [--force]removes containers/environments so they get recreated with current config on next use.- See Sandbox vs Tool Policy vs Elevated for the “why is this blocked?” mental model.
Multi-agent overrides
Each agent can override sandbox + tools:agents.list[].sandbox and agents.list[].tools (plus agents.list[].tools.sandbox.tools for sandbox tool policy). See Multi-Agent Sandbox & Tools for precedence.
Minimal enable example
Related
- Multi-Agent Sandbox & Tools — per-agent overrides and precedence
- OpenShell — managed sandbox backend setup, workspace modes, and config reference
- Sandbox configuration
- Sandbox vs Tool Policy vs Elevated — debugging “why is this blocked?”
- Security