2026-01-08 21:49:26 +01:00
---
2026-01-30 03:15:10 +01:00
summary: "How OpenClaw sandboxing works: modes, scopes, workspace access, and images"
2026-01-08 21:49:26 +01:00
title: Sandboxing
2026-01-09 12:44:23 +00:00
read_when: "You want a dedicated explanation of sandboxing or need to tune agents.defaults.sandbox."
2026-01-08 21:49:26 +01:00
status: active
---
# Sandboxing
2026-01-30 03:15:10 +01:00
OpenClaw can run **tools inside Docker containers** to reduce blast radius.
2026-01-09 12:44:23 +00:00
This is **optional** and controlled by configuration (`agents.defaults.sandbox` or
`agents.list[].sandbox` ). If sandboxing is off, tools run on the host.
2026-01-08 21:51:21 +01:00
The Gateway stays on the host; tool execution runs in an isolated sandbox
when enabled.
2026-01-08 21:49:26 +01:00
This is not a perfect security boundary, but it materially limits filesystem
and process access when the model does something dumb.
## What gets sandboxed
2026-01-31 21:13:13 +09:00
2026-01-12 03:42:49 +00:00
- Tool execution (`exec` , `read` , `write` , `edit` , `apply_patch` , `process` , etc.).
2026-01-09 12:44:23 +00:00
- Optional sandboxed browser (`agents.defaults.sandbox.browser` ).
2026-01-10 02:06:05 +00:00
- By default, the sandbox browser auto-starts (ensures CDP is reachable) when the browser tool needs it.
Configure via `agents.defaults.sandbox.browser.autoStart` and `agents.defaults.sandbox.browser.autoStartTimeoutMs` .
2026-02-21 14:01:40 +01:00
- By default, sandbox browser containers use a dedicated Docker network (`openclaw-sandbox-browser` ) instead of the global `bridge` network.
Configure with `agents.defaults.sandbox.browser.network` .
- Optional `agents.defaults.sandbox.browser.cdpSourceRange` restricts container-edge CDP ingress with a CIDR allowlist (for example `172.21.0.1/32` ).
2026-03-01 22:44:15 +00:00
- 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 password in URL fragment (not query/header logs).
2026-01-11 01:24:02 +01:00
- `agents.defaults.sandbox.browser.allowHostControl` lets sandboxed sessions target the host browser explicitly.
2026-01-11 01:52:23 +01:00
- Optional allowlists gate `target: "custom"` : `allowedControlUrls` , `allowedControlHosts` , `allowedControlPorts` .
2026-01-08 21:49:26 +01:00
Not sandboxed:
2026-01-31 21:13:13 +09:00
2026-01-08 21:49:26 +01:00
- The Gateway process itself.
2026-01-09 12:44:23 +00:00
- Any tool explicitly allowed to run on the host (e.g. `tools.elevated` ).
2026-01-12 02:49:55 +00:00
- **Elevated exec runs on the host and bypasses sandboxing.**
2026-01-09 12:44:23 +00:00
- If sandboxing is off, `tools.elevated` does not change execution (already on host). See [Elevated Mode ](/tools/elevated ).
2026-01-08 21:49:26 +01:00
## Modes
2026-01-31 21:13:13 +09:00
2026-01-09 12:44:23 +00:00
`agents.defaults.sandbox.mode` controls **when** sandboxing is used:
2026-01-31 21:13:13 +09:00
2026-01-08 21:49:26 +01:00
- `"off"` : no sandboxing.
- `"non-main"` : sandbox only **non-main** sessions (default if you want normal chats on host).
- `"all"` : every session runs in a sandbox.
2026-01-31 21:13:13 +09:00
Note: `"non-main"` is based on `session.mainKey` (default `"main"` ), not agent id.
Group/channel sessions use their own keys, so they count as non-main and will be sandboxed.
2026-01-08 21:49:26 +01:00
## Scope
2026-01-31 21:13:13 +09:00
2026-01-09 12:44:23 +00:00
`agents.defaults.sandbox.scope` controls **how many containers** are created:
2026-01-31 21:13:13 +09:00
2026-01-08 21:49:26 +01:00
- `"session"` (default): one container per session.
- `"agent"` : one container per agent.
- `"shared"` : one container shared by all sandboxed sessions.
## Workspace access
2026-01-31 21:13:13 +09:00
2026-01-09 12:44:23 +00:00
`agents.defaults.sandbox.workspaceAccess` controls **what the sandbox can see** :
2026-01-31 21:13:13 +09:00
2026-01-30 03:15:10 +01:00
- `"none"` (default): tools see a sandbox workspace under `~/.openclaw/sandboxes` .
2026-01-12 03:42:49 +00:00
- `"ro"` : mounts the agent workspace read-only at `/agent` (disables `write` /`edit` /`apply_patch` ).
2026-01-08 21:49:26 +01:00
- `"rw"` : mounts the agent workspace read/write at `/workspace` .
Inbound media is copied into the active sandbox workspace (`media/inbound/*` ).
2026-01-09 00:30:51 +01:00
Skills note: the `read` tool is sandbox-rooted. With `workspaceAccess: "none"` ,
2026-01-30 03:15:10 +01:00
OpenClaw mirrors eligible skills into the sandbox workspace (`.../skills` ) so
2026-01-09 00:30:51 +01:00
they can be read. With `"rw"` , workspace skills are readable from
`/workspace/skills` .
2026-01-08 21:49:26 +01:00
2026-01-12 10:13:32 -07:00
## Custom bind mounts
2026-01-31 21:13:13 +09:00
2026-01-12 10:13:32 -07:00
`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.
2026-02-14 23:27:41 +09:00
`agents.defaults.sandbox.browser.binds` mounts additional host directories into the **sandbox browser** container only.
- When set (including `[]` ), it replaces `agents.defaults.sandbox.docker.binds` for the browser container.
- When omitted, the browser container falls back to `agents.defaults.sandbox.docker.binds` (backwards compatible).
2026-02-16 03:05:16 +01:00
Example (read-only source + an extra data directory):
2026-01-12 22:06:17 +00:00
```json5
{
agents: {
defaults: {
sandbox: {
docker: {
2026-02-16 03:05:16 +01:00
binds: ["/home/user/source:/source:ro", "/var/data/myapp:/data:ro"],
2026-01-31 21:13:13 +09:00
},
},
2026-01-12 22:06:17 +00:00
},
list: [
{
id: "build",
sandbox: {
docker: {
2026-01-31 21:13:13 +09:00
binds: ["/mnt/cache:/cache:rw"],
},
},
},
],
},
2026-01-12 22:06:17 +00:00
}
```
Security notes:
2026-01-31 21:13:13 +09:00
2026-01-12 22:06:17 +00:00
- Binds bypass the sandbox filesystem: they expose host paths with whatever mode you set (`:ro` or `:rw` ).
2026-02-16 03:05:16 +01:00
- OpenClaw blocks dangerous bind sources (for example: `docker.sock` , `/etc` , `/proc` , `/sys` , `/dev` , and parent mounts that would expose them).
- Sensitive mounts (secrets, SSH keys, service credentials) should be `:ro` unless absolutely required.
2026-01-12 22:06:17 +00:00
- Combine with `workspaceAccess: "ro"` if you only need read access to the workspace; bind modes stay independent.
- See [Sandbox vs Tool Policy vs Elevated ](/gateway/sandbox-vs-tool-policy-vs-elevated ) for how binds interact with tool policy and elevated exec.
2026-01-08 21:49:26 +01:00
## Images + setup
2026-01-31 21:13:13 +09:00
2026-01-30 03:15:10 +01:00
Default image: `openclaw-sandbox:bookworm-slim`
2026-01-08 21:49:26 +01:00
Build it once:
2026-01-31 21:13:13 +09:00
2026-01-08 21:49:26 +01:00
```bash
scripts/sandbox-setup.sh
```
2026-01-20 15:00:03 +00:00
Note: 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).
2026-03-01 23:16:00 -08:00
If you want a more functional sandbox image with common tooling (for example
`curl` , `jq` , `nodejs` , `python3` , `git` ), build:
```bash
scripts/sandbox-common-setup.sh
```
Then set `agents.defaults.sandbox.docker.image` to
`openclaw-sandbox-common:bookworm-slim` .
2026-01-08 21:49:26 +01:00
Sandboxed browser image:
2026-01-31 21:13:13 +09:00
2026-01-08 21:49:26 +01:00
```bash
scripts/sandbox-browser-setup.sh
```
By default, sandbox containers run with **no network** .
2026-01-09 12:44:23 +00:00
Override with `agents.defaults.sandbox.docker.network` .
2026-01-08 21:49:26 +01:00
2026-02-24 23:19:48 +00:00
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` .
2026-01-08 21:49:26 +01:00
Docker installs and the containerized gateway live here:
[Docker ](/install/docker )
2026-03-01 23:16:00 -08:00
For Docker gateway deployments, `docker-setup.sh` can bootstrap sandbox config.
Set `OPENCLAW_SANDBOX=1` (or `true` /`yes` /`on` ) to enable that path. You can
override socket location with `OPENCLAW_DOCKER_SOCKET` . Full setup and env
reference: [Docker ](/install/docker#enable-agent-sandbox-for-docker-gateway-opt-in ).
2026-01-19 01:35:17 +00:00
## setupCommand (one-time container setup)
2026-01-31 21:13:13 +09:00
2026-01-19 01:35:17 +00:00
`setupCommand` runs **once** after the sandbox container is created (not on every run).
It executes inside the container via `sh -lc` .
Paths:
2026-01-31 21:13:13 +09:00
2026-01-19 01:35:17 +00:00
- Global: `agents.defaults.sandbox.docker.setupCommand`
- Per-agent: `agents.list[].sandbox.docker.setupCommand`
Common pitfalls:
2026-01-31 21:13:13 +09:00
2026-01-19 01:35:17 +00:00
- Default `docker.network` is `"none"` (no egress), so package installs will fail.
2026-02-24 23:19:48 +00:00
- `docker.network: "container:<id>"` requires `dangerouslyAllowContainerNamespaceJoin: true` and is break-glass only.
2026-01-19 01:35:17 +00:00
- `readOnlyRoot: true` prevents writes; set `readOnlyRoot: false` or bake a custom image.
- `user` must be root for package installs (omit `user` or set `user: "0:0"` ).
2026-01-20 15:00:03 +00:00
- Sandbox exec does **not** inherit host `process.env` . Use
`agents.defaults.sandbox.docker.env` (or a custom image) for skill API keys.
2026-01-19 01:35:17 +00:00
2026-01-08 21:49:26 +01:00
## Tool policy + escape hatches
2026-01-31 21:13:13 +09:00
2026-01-08 21:49:26 +01:00
Tool allow/deny policies still apply before sandbox rules. If a tool is denied
globally or per-agent, sandboxing doesn’ t bring it back.
2026-01-12 02:49:55 +00:00
`tools.elevated` is an explicit escape hatch that runs `exec` on the host.
2026-01-26 22:18:36 +00:00
`/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 ](/gateway/sandbox-vs-tool-policy-vs-elevated )).
2026-01-10 20:28:34 +01:00
Debugging:
2026-01-31 21:13:13 +09:00
2026-01-30 03:15:10 +01:00
- Use `openclaw sandbox explain` to inspect effective sandbox mode, tool policy, and fix-it config keys.
2026-01-10 20:28:34 +01:00
- See [Sandbox vs Tool Policy vs Elevated ](/gateway/sandbox-vs-tool-policy-vs-elevated ) for the “why is this blocked?” mental model.
2026-01-31 21:13:13 +09:00
Keep it locked down.
2026-01-08 21:49:26 +01:00
## Multi-agent overrides
2026-01-31 21:13:13 +09:00
2026-01-08 21:49:26 +01:00
Each agent can override sandbox + tools:
2026-01-09 12:44:23 +00:00
`agents.list[].sandbox` and `agents.list[].tools` (plus `agents.list[].tools.sandbox.tools` for sandbox tool policy).
2026-02-07 15:40:35 -05:00
See [Multi-Agent Sandbox & Tools ](/tools/multi-agent-sandbox-tools ) for precedence.
2026-01-08 21:49:26 +01:00
## Minimal enable example
2026-01-31 21:13:13 +09:00
2026-01-08 21:49:26 +01:00
```json5
{
2026-01-09 12:44:23 +00:00
agents: {
defaults: {
sandbox: {
mode: "non-main",
scope: "session",
2026-01-31 21:13:13 +09:00
workspaceAccess: "none",
},
},
},
2026-01-08 21:49:26 +01:00
}
```
## Related docs
2026-01-31 21:13:13 +09:00
2026-01-09 12:44:23 +00:00
- [Sandbox Configuration ](/gateway/configuration#agentsdefaults-sandbox )
2026-02-07 15:40:35 -05:00
- [Multi-Agent Sandbox & Tools ](/tools/multi-agent-sandbox-tools )
2026-01-08 21:49:26 +01:00
- [Security ](/gateway/security )