Sandboxing
OpenClaw can run tools inside sandbox backends to reduce blast radius. This is optional and controlled by configuration (agents.defaults.sandbox or
agents.list[].sandbox). If sandboxing is off, tools run on the host.
The Gateway stays on the host; tool execution runs in an isolated 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.). - Optional sandboxed browser (
agents.defaults.sandbox.browser).- By default, the sandbox browser auto-starts (ensures CDP is reachable) when the browser tool needs it.
Configure via
agents.defaults.sandbox.browser.autoStartandagents.defaults.sandbox.browser.autoStartTimeoutMs. - By default, sandbox browser containers use a dedicated Docker network (
openclaw-sandbox-browser) instead of the globalbridgenetwork. Configure withagents.defaults.sandbox.browser.network. - Optional
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 password in URL fragment (not query/header logs).
agents.defaults.sandbox.browser.allowHostControllets sandboxed sessions target the host browser explicitly.- Optional allowlists gate
target: "custom":allowedControlUrls,allowedControlHosts,allowedControlPorts.
- By default, the sandbox browser auto-starts (ensures CDP is reachable) when the browser tool needs it.
Configure via
- The Gateway process itself.
- Any tool explicitly allowed to run on the host (e.g.
tools.elevated).- Elevated exec runs on the host and bypasses sandboxing.
- If sandboxing is off,
tools.elevateddoes not change execution (already on host). See Elevated Mode.
Modes
agents.defaults.sandbox.mode controls when sandboxing is used:
"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. Note:"non-main"is based onsession.mainKey(default"main"), not agent id. Group/channel sessions use their own keys, so they count as non-main and will be sandboxed.
Scope
agents.defaults.sandbox.scope controls how many containers are created:
"session"(default): one container per session."agent": one container per agent."shared": one container shared by all sandboxed sessions.
Backend
agents.defaults.sandbox.backend controls which runtime provides the sandbox:
"docker"(default): local Docker-backed sandbox runtime."ssh": generic SSH-backed remote sandbox runtime."openshell": OpenShell-backed sandbox runtime.
agents.defaults.sandbox.ssh.
OpenShell-specific config lives under plugins.entries.openshell.config.
SSH backend
Usebackend: "ssh" when you want OpenClaw to sandbox exec, file tools, and media reads on
an arbitrary SSH-accessible machine.
- OpenClaw creates a per-scope remote root under
sandbox.ssh.workspaceRoot. - On first use after create or recreate, OpenClaw 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.
identityFile,certificateFile,knownHostsFile: use existing local files and pass them through OpenSSH config.identityData,certificateData,knownHostsData: use inline strings or SecretRefs. OpenClaw resolves them through the normal secrets runtime snapshot, writes them to temp files with0600, and deletes them when the SSH session ends.- If both
*Fileand*Dataare set for the same item,*Datawins for that SSH session.
- 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 the SSH backend.
sandbox.docker.*settings do not apply to the SSH backend.
mirror(default): local workspace stays canonical. OpenClaw syncs local files into OpenShell before exec and syncs the remote workspace back after exec.remote: OpenShell workspace is canonical after the sandbox is created. OpenClaw seeds the remote workspace once from the local workspace, then file tools and exec run directly against the remote sandbox without syncing changes back.
sandbox create/get/delete, sandbox ssh-config) and the optional mirror mode.
Remote transport details:
- OpenClaw asks OpenShell for sandbox-specific SSH config via
openshell sandbox ssh-config <name>. - Core writes that SSH config to a temp file, opens the SSH session, and reuses the same remote filesystem bridge used by
backend: "ssh". - In
mirrormode only the lifecycle differs: sync local to remote before exec, then sync back after exec.
- sandbox browser is not supported yet
sandbox.docker.bindsis not supported on the OpenShell backend- Docker-specific runtime knobs under
sandbox.docker.*still apply only to the Docker backend
OpenShell workspace modes
OpenShell has two workspace models. This is the part that matters most in practice.mirror
Use plugins.entries.openshell.config.mode: "mirror" when you want the local workspace to stay canonical.
Behavior:
- Before
exec, OpenClaw syncs the local workspace into the OpenShell sandbox. - After
exec, OpenClaw syncs the remote workspace back to the local workspace. - File tools still operate through the sandbox bridge, but the local workspace remains the source of truth between turns.
- you edit files locally outside OpenClaw and want those changes to show up in the sandbox automatically
- you want the OpenShell sandbox to behave as much like the Docker backend as possible
- you want the host workspace to reflect sandbox writes after each exec turn
- extra sync cost before and after exec
remote
Use plugins.entries.openshell.config.mode: "remote" when you want the OpenShell workspace to become canonical.
Behavior:
- When the sandbox is first created, OpenClaw seeds the remote workspace from the local workspace once.
- After that,
exec,read,write,edit, andapply_patchoperate directly against the remote OpenShell workspace. - OpenClaw does not sync remote changes back into the local workspace after exec.
- Prompt-time media reads still work because file and media tools read through the sandbox bridge instead of assuming a local host path.
- Transport is SSH into the OpenShell sandbox returned by
openshell sandbox ssh-config.
- If you edit files on the host outside OpenClaw after the seed step, the remote sandbox will not see those changes automatically.
- If the sandbox is recreated, the remote workspace is seeded from the local workspace again.
- With
scope: "agent"orscope: "shared", that remote workspace is shared at that same scope.
- the sandbox should live primarily on the remote OpenShell side
- you want lower per-turn sync overhead
- you do not want host-local edits to silently overwrite remote sandbox state
mirror if you think of the sandbox as a temporary execution environment.
Choose remote if you think of the sandbox as the real workspace.
OpenShell lifecycle
OpenShell sandboxes are still managed through the normal sandbox lifecycle:openclaw sandbox listshows OpenShell runtimes as well as Docker runtimesopenclaw sandbox recreatedeletes the current runtime and lets OpenClaw recreate it on next use- prune logic is backend-aware too
remote mode, recreate is especially important:
- recreate deletes the canonical remote workspace for that scope
- the next use seeds a fresh remote workspace from the local workspace
mirror mode, recreate mainly resets the remote execution environment
because the local workspace remains canonical anyway.
Workspace access
agents.defaults.sandbox.workspaceAccess controls what the sandbox can see:
"none"(default): tools see a sandbox workspace under~/.openclaw/sandboxes."ro": mounts the agent workspace read-only at/agent(disableswrite/edit/apply_patch)."rw": mounts the agent workspace read/write at/workspace.
mirrormode still uses the local workspace as the canonical source between exec turnsremotemode uses the remote OpenShell workspace as the canonical source after the initial seedworkspaceAccess: "ro"and"none"still restrict write behavior the same way
media/inbound/*).
Skills note: 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.
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 replacesagents.defaults.sandbox.docker.bindsfor the browser container. - When omitted, the browser container falls back to
agents.defaults.sandbox.docker.binds(backwards compatible).
- Binds bypass the sandbox filesystem: they expose host paths with whatever mode you set (
:roor:rw). - 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
:rounless absolutely required. - Combine with
workspaceAccess: "ro"if you only need read access to the workspace; bind modes stay independent. - See Sandbox vs Tool Policy vs Elevated for how binds interact with tool policy and elevated exec.
Images + setup
Default Docker image:openclaw-sandbox:bookworm-slim
Build it once:
sandbox.docker.setupCommand (requires network egress + writable root +
root user).
If you want a more functional sandbox image with common tooling (for example
curl, jq, nodejs, python3, git), build:
agents.defaults.sandbox.docker.image to
openclaw-sandbox-common:bookworm-slim.
Sandboxed browser image:
agents.defaults.sandbox.docker.network.
The bundled sandbox browser image also applies conservative Chromium startup defaults
for containerized workloads. Current container defaults include:
--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-3d-apis--disable-gpu--disable-dev-shm-usage--disable-background-networking--disable-extensions--disable-features=TranslateUI--disable-breakpad--disable-crash-reporter--disable-software-rasterizer--no-zygote--metrics-recording-only--renderer-process-limit=2--no-sandboxand--disable-setuid-sandboxwhennoSandboxis enabled.- The three graphics hardening flags (
--disable-3d-apis,--disable-software-rasterizer,--disable-gpu) are optional and are useful when containers lack GPU support. SetOPENCLAW_BROWSER_DISABLE_GRAPHICS_FLAGS=0if your workload requires WebGL or other 3D/browser features. --disable-extensionsis enabled by default and can be disabled withOPENCLAW_BROWSER_DISABLE_EXTENSIONS=0for extension-reliant flows.--renderer-process-limit=2is controlled byOPENCLAW_BROWSER_RENDERER_PROCESS_LIMIT=<N>, where0keeps Chromium’s default.
browser.extraArgs to append additional startup flags.
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.
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.
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
- 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.
Tool policy + 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 on the host.
/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:
- Use
openclaw sandbox explainto inspect effective sandbox mode, tool policy, and fix-it config keys. - See Sandbox vs Tool Policy vs Elevated for the “why is this blocked?” mental model. Keep it locked down.
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.