OpenClaw Sandboxing: Modes, Scopes, and Workspace Access
Learn how OpenClaw sandboxing limits tool execution impact, including modes, scopes, workspace access, and images. Essential for administrators configuring agent security.
OpenClaw supports running tool execution within a sandbox backend to limit the potential impact of a failure. This feature is disabled unless explicitly turned on, and it is managed through agents.defaults.sandbox at the global level or agents.entries.*.sandbox for individual agents. The Gateway process remains on the host at all times; only tool execution is moved into the sandbox when the feature is active.
Note
Treat this as a practical mitigation rather than a hard security guarantee. It does, however, meaningfully restrict filesystem and process access when the model acts in an unintended way.
What gets sandboxed
- Tool execution:
exec,read,write,edit,apply_patch,process, and others. - The optional sandboxed browser (
agents.defaults.sandbox.browser).
Not sandboxed:
- The Gateway process itself.
- Any tool that is explicitly permitted to run outside the sandbox through
tools.elevated. Elevated exec skips sandboxing and uses the configured escape path (gatewayby default, ornodewhen the exec target isnode). With sandboxing disabled,tools.elevatedhas no effect because exec already operates on the host. Refer to Elevated Mode for details.
Modes, scope, and backend
Sandbox behavior is governed by three separate settings:
| 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, podman, ssh, openshell | docker |
Mode determines when sandboxing is applied:
off: sandboxing is turned off.non-main: every session gets sandboxed except the agent's main session. The main session key is fixed atagent:<agentId>:main(orglobalwhensession.scopeis set to"global"); it cannot be changed. Group and channel sessions use their own keys, so they are always treated as non-main and sandboxed.all: sandboxing applies to every session.
Scope controls how many containers or environments get created:
agent: a single container per agent.session: a single container per session.shared: one container shared across all sandboxed sessions (per-agentdocker/ssh/browseroverrides are ignored in this scope).
The resolved agent workspace path also forms part of the non-shared runtime identity. This stops co-hosted workspaces that reuse the same agent or session keys from sharing Docker, browser, SSH, OpenShell, or plugin-provided sandbox state. The shared scope is deliberately independent of the workspace.
After an upgrade from an older release, the first use creates non-shared runtimes and sandbox workspaces under the workspace-qualified identity. Existing non-shared runtimes are not picked up; this reset is intentional and happens once. They can expire through configured prune settings or be cleared with openclaw sandbox recreate; the next use provisions the current identity.
Backend selects which runtime executes sandboxed tools. Docker and Podman share agents.defaults.sandbox.docker; SSH-specific configuration sits under agents.defaults.sandbox.ssh; OpenShell-specific configuration sits under plugins.entries.openshell.config.
| Docker or Podman backend | SSH | OpenShell | |
|---|---|---|---|
| Where it runs | Local Docker or Podman container | Any SSH-accessible host | OpenShell managed sandbox |
| Setup | Docker and/or Podman | 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 | Docker engine only | Not supported | Not supported yet |
| Bind mounts | docker.binds | N/A | N/A |
| Best for | Local development and container isolation | Offloading to a remote machine | Managed remote sandboxes with optional two-way sync |
Supported capability matrix
Sandbox backends provide isolation for tool execution. They do not relocate the Gateway, native plugins, or control-plane RPC into the sandbox.
| Capability | Docker | SSH | OpenShell |
|---|---|---|---|
| Shell and child processes | Supported inside the container | Supported on the remote host | Supported inside the managed sandbox |
| File tools | Supported through the container filesystem bridge | Supported through the SSH filesystem bridge | Supported through the SSH bridge in mirror or remote mode |
| Workspace access | none, ro, and rw | none, ro, and rw | none, ro, and rw |
| Network restriction | docker.network; defaults to "none" | Controlled by the remote host | Controlled by the selected OpenShell policy |
| Sandboxed browser | Supported in a separate browser container | Not supported | Not supported |
| Additional host folders | docker.binds with explicit :ro or :rw | Not supported as mounts; seed or copy files instead | Not supported as mounts; use workspace sync or remote files |
| Packages and runtimes | Bake a custom image, or use setupCommand with the required privileges | Provision them on the remote host | Include them in the source image or install when policy permits |
| Private certificate roots | Bake or mount them into the image and configure the consuming runtime | Configure the remote host trust store | Include them in the source image or configure them inside sandbox |
| Plugin and MCP tool access | Gateway-side execution, additionally gated by sandbox tool policy | Gateway-side execution, additionally gated by policy | Gateway-side execution, additionally gated by sandbox tool policy |
Native plugins run in-process with the Gateway and operate within the same trust boundary. For sandboxed sessions, plugin-owned and MCP tools are accessible only when standard tool policy and tools.sandbox.tools both permit them. Refer to MCP and plugin tools inside sandbox tool policy and Plugin execution model for details.
Docker backend
The Docker backend executes tools locally via the docker CLI. Its selection and error handling remain the same; it does not probe or fall back to Podman.
Defaults: network: "none" (no egress), readOnlyRoot: true, capDrop: ["ALL"], image openclaw-sandbox:bookworm-slim.
This explicit configuration keeps the agent workspace read-only and preserves the default restricted runtime posture:
{
agents: {
defaults: {
sandbox: {
mode: "all",
backend: "docker",
scope: "session",
workspaceAccess: "ro",
docker: {
image: "openclaw-sandbox:bookworm-slim",
readOnlyRoot: true,
tmpfs: ["/tmp", "/var/tmp", "/run"],
network: "none",
capDrop: ["ALL"],
},
},
},
},
}
OpenClaw also provisions Docker sandbox containers with an init process and no-new-privileges. With workspaceAccess: "ro", the agent workspace is mounted read-only at /agent; write attempts to the agent workspace are denied, while the configured tmpfs paths stay writable.
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 forwarded to the chosen container engine's Docker-compatible --gpus flag and requires compatible host GPU setup. Podman needs version 5.0 or newer for this option.
Warning
Docker-out-of-Docker (DooD) constraints
If the OpenClaw Gateway itself is deployed as a Docker container, it orchestrates sibling sandbox containers through the host's Docker socket (DooD). This creates a path mapping constraint:
- Config requires host paths:
openclaw.jsonworkspacemust contain the host's absolute path (e.g./home/user/.openclaw/workspaces), not the internal Gateway container path. The Docker daemon resolves paths relative to the host OS namespace, not the Gateway's own namespace.- Matching volume map required: The Gateway process also writes heartbeat and bridge files to that
workspacepath. Give the Gateway container an identical volume map (-v /home/user/.openclaw:/home/user/.openclaw) so the same host path resolves correctly from inside the Gateway container too. Mismatched mappings surface asEACCESwhen the Gateway tries to write its heartbeat.- Codex code mode: when an OpenClaw sandbox is active, OpenClaw disables Codex app-server native Code Mode, user MCP servers, and app-backed plugin execution for that turn (those run from the Gateway-host app-server process, not the OpenClaw sandbox backend), unless the sandbox tool policy exposes the required tools and you opt into the experimental sandbox exec-server path. Shell access then routes through OpenClaw sandbox-backed tools such as
sandbox_execandsandbox_process. Do not mount the host Docker socket into agent sandbox containers or custom Codex sandboxes. See Codex Harness for the full behavior.On Ubuntu/AppArmor hosts with Docker sandbox mode enabled, Codex app-server
workspace-writeshell execution needs unprivileged user namespaces inside the sandbox container, and this can fail before shell startup when the service user cannot create them. This needs an unprivileged network namespace too when Docker sandbox egress is disabled (network: "none", the default). Common symptoms:bwrap: setting up uid map: Permission deniedandbwrap: loopback: Failed RTM_NEWADDR: Operation not permitted. Runopenclaw doctor; if it reports a Codex bwrap namespace probe failure, prefer an AppArmor profile that grants the required namespaces to the OpenClaw service process.kernel.apparmor_restrict_unprivileged_userns=0is a host-wide fallback with security tradeoffs; use it only when that host posture is acceptable.
Sandboxed browser
- When the browser tool requires it, the sandbox browser launches automatically, guaranteeing that CDP is reachable. Tune this behavior with
agents.defaults.sandbox.browser.autoStart(defaulting totrue) andautoStartTimeoutMs(defaulting to 12s). - Sandbox browser containers operate on a dedicated Docker network called
openclaw-sandbox-browser, not the globalbridgenetwork. Set this up viaagents.defaults.sandbox.browser.network. - The
"none"network mode for sandbox browsers is not supported, since browser control depends on CDP ports published to the host. Stick with the dedicated default,bridge, or pick another custom bridge network.openclaw doctor --fixturns off affected persisted sidecars and brings back the dedicated network, without quietly enabling egress. - With
agents.defaults.sandbox.browser.cdpSourceRange, container-edge CDP ingress is limited by a CIDR allowlist, for instance172.21.0.1/32. - By default, noVNC observer access requires a password; OpenClaw provides a short-lived token URL that loads a local bootstrap page and starts noVNC with the password placed in the URL fragment, not in the query string or header logs.
- Sandboxed sessions can explicitly target the host browser using
agents.defaults.sandbox.browser.allowHostControl(defaulting tofalse). - Optional allowlists control access to
target: "custom"viaallowedControlUrls,allowedControlHosts, andallowedControlPorts.
Podman backend
Pick the native podman CLI directly with sandbox.backend: "podman". This backend ships with the product, not as a plugin. It never probes or picks Docker, even if the docker executable is present.
Podman relies on the current sandbox.docker.* settings and the active native podman CLI context; there is no separate connection configuration surface for it.
In rootless mode, Podman uses --userns=keep-id by default for writable workspace mounts. A long-running sandbox can hold subordinate IDs and block unrelated --userns=auto workloads; remove it before launching those workloads. Set sandbox.docker.user to a nonzero numeric UID or UID:GID to dictate the container user. Rootless Podman refuses UID or GID 0, because Podman 4.x cannot remap namespace root while keeping workspace bind ownership intact; put root-required setup into the image or switch to rootful Podman. Otherwise, rootful Podman uses the workspace owner when available.
{
agents: {
defaults: {
sandbox: {
mode: "all",
backend: "podman",
scope: "session",
workspaceAccess: "rw",
docker: {
image: "openclaw-sandbox:bookworm-slim",
network: "none",
readOnlyRoot: true,
capDrop: ["ALL"],
},
},
},
},
}
Before enabling the backend, build or pull the sandbox image into the Podman store you selected. From a source checkout, build the same sandbox Dockerfile with Podman:
podman build -t openclaw-sandbox:bookworm-slim -f scripts/docker/sandbox/Dockerfile .
Podman notes:
- Podman does not support browser sandboxing; leave
sandbox.browser.enabledoff, or install Docker and choosebackend: "docker". - Local Podman engines and Podman Machine work fine. For Podman Machine, bind sources must sit under the host home directory, which is its default shared volume. Arbitrary remote Podman connections are not accepted; use the SSH backend for remote execution.
- Custom
tmpfsor bind mounts must not overlap/run/podman-init; OpenClaw rejects them so sandbox cleanup keeps functioning.
Warning
Podman-outside-of-Podman constraints
A containerized Gateway creates sibling sandboxes via the host's local Podman engine or Podman Machine.
- Use host paths consistently: set
workspaceto its host absolute path, then mount the full state root and workspace into the Gateway at those same paths. Otherwise, the sandbox might mount the workspace while the Gateway cannot write heartbeat or skill-workspace files.- Podman Machine setup: bind sources must be under the host home directory. Point the Gateway
HOMEat that path and directOPENCLAW_HOME,OPENCLAW_STATE_DIR, andOPENCLAW_CONFIG_DIRto the canonical mounted state root. The image needs a compatible Podman client, its named connection and SSH identity, plus a dedicated writable SSH directory for known-host metadata.- Keep Podman access Gateway-only: never mount the engine socket, connection material, or SSH identity into agent sandboxes. Arbitrary remote connections are unsupported; use the SSH backend instead.
SSH backend
Use backend: "ssh" to sandbox exec, file tools, and media reads on any SSH-accessible machine.
{
agents: {
defaults: {
sandbox: {
mode: "all",
backend: "ssh",
scope: "session",
workspaceAccess: "rw",
ssh: {
target: "user@gateway-host:22",
workspaceRoot: "/tmp/openclaw-sandboxes",
strictHostKeyChecking: true,
updateHostKeys: true,
identityFile: "~/.ssh/id_ed25519",
certificateFile: "~/.ssh/id_ed25519-cert.pub",
knownHostsFile: "~/.ssh/known_hosts",
// Or use SecretRefs / inline contents instead of local files:
// identityData: { source: "env", provider: "default", id: "SSH_IDENTITY" },
// certificateData: { source: "env", provider: "default", id: "SSH_CERTIFICATE" },
// knownHostsData: { source: "env", provider: "default", id: "SSH_KNOWN_HOSTS" },
},
},
},
},
}
Defaults: command: "ssh", workspaceRoot: "/tmp/openclaw-sandboxes", strictHostKeyChecking: true, updateHostKeys: true.
- Lifecycle: A per-scope remote root is set up by OpenClaw at
sandbox.ssh.workspaceRoot. The first time it is used after being created or recreated, that remote workspace gets seeded from the local workspace in a one-time operation. From then on,exec,read,write,edit,apply_patch, reads of prompt media, and staging of inbound media all operate directly against the remote workspace through SSH. No automatic syncing of remote changes back to the local workspace is performed by OpenClaw. - Authentication material: Local files are referenced by
identityFile/certificateFile/knownHostsFile. Inline strings or SecretRefs are accepted byidentityData/certificateData/knownHostsData, resolved via the standard secrets runtime snapshot, written to temporary files with mode0600, and removed once the SSH session ends. When both a*Fileand a*Datavariant are configured for the same item,*Datatakes precedence for that session. - Remote-canonical consequences: After the initial seed, the remote SSH workspace holds the true sandbox state. Edits made locally on the host outside OpenClaw following the seed step stay invisible remotely until the sandbox is recreated. Deleting the per-scope remote root and reseeding from local on the next use is what
openclaw sandbox recreatedoes. This backend does not support browser sandboxing, andsandbox.docker.*settings have no effect on it.
OpenShell backend
Sandboxing tools in an OpenShell-managed remote environment is done with backend: "openshell". The same SSH transport and remote filesystem bridge as the generic SSH backend are reused by OpenShell, which also introduces OpenShell lifecycle (sandbox create/get/delete/ssh-config) and an optional mirror workspace sync mode.
{
agents: {
defaults: {
sandbox: {
mode: "all",
backend: "openshell",
scope: "session",
workspaceAccess: "rw",
},
},
},
plugins: {
entries: {
openshell: {
enabled: true,
config: {
from: "openclaw",
mode: "remote", // mirror | remote
},
},
},
},
}
With mode: "mirror" (the default), the local workspace stays canonical: OpenClaw pushes local content into the sandbox ahead of exec and pulls it back afterward. mode: "remote" seeds the remote workspace from local once, then runs exec/read/write/edit/apply_patch directly against the remote workspace with no syncing back; local edits made after the seed remain hidden until you openclaw sandbox recreate. Under either scope: "agent" or scope: "shared", that remote workspace is shared at the same scope. Current constraints: the sandbox browser is not yet available, and sandbox.docker.binds does not apply to this backend.
OpenShell runtimes are treated the same as Docker runtimes by openclaw sandbox list/recreate/prune, with prune logic being backend-aware.
Refer to OpenShell for the full prerequisites, configuration reference, workspace-mode comparison, and lifecycle details.
Workspace access
What the sandbox can see is governed by agents.defaults.sandbox.workspaceAccess:
| Value | Behavior |
|---|---|
none (default) | Tools are presented with an isolated sandbox workspace at ~/.openclaw/sandboxes. |
ro | The agent workspace is mounted read-only at /agent (write/edit/apply_patch are disabled). |
rw | The agent workspace is mounted read/write at /workspace. |
On the OpenShell backend, mirror mode continues to treat the local workspace as canonical between exec turns, remote mode makes the remote OpenShell workspace canonical after the initial seed, and workspaceAccess: "ro"/"none" still constrain write behavior in the same manner.
The active sandbox workspace (media/inbound/*) receives copies of inbound media.
Note
Skills: the
readtool is rooted in the sandbox. WithworkspaceAccess: "none", OpenClaw mirrors eligible skills into the sandbox workspace (.../skills) so they can be accessed. With"rw", workspace skills are readable from/workspace/skills, and eligible managed, bundled, or plugin skills get materialized into the generated read-only path/workspace/.openclaw/sandbox-skills/skills.
Multiple folders for one agent
When a sandboxed agent needs access beyond its main workspace, Docker bind mounts are the way to go. Each mount pairs a host directory with a container path and specifies an explicit access level:
host-directory:container-directory:ro
host-directory:container-directory:rw
- With
ro, the mounted folder is read-only within the sandbox. rwallows sandboxed tools and processes to modify the host folder.- The container path is what the agent references. Host paths are never directly exposed.
In this setup, the research agent gets a writable primary workspace, read-only reference material at /reference, and a separate writable output folder at /drafts:
{
agents: {
defaults: {
sandbox: {
mode: "all",
scope: "agent",
},
},
entries: {
research: {
default: true,
workspace: "/srv/openclaw/research-workspace",
sandbox: {
workspaceAccess: "rw",
docker: {
binds: ["/srv/shared/reference:/reference:ro", "/srv/shared/drafts:/drafts:rw"],
// Required because these sources are outside the agent workspace.
dangerouslyAllowExternalBindSources: true,
},
},
},
},
},
}
workspaceAccess operates independently of bind modes:
| Setting | Controls |
|---|---|
workspaceAccess: "none" | Uses an isolated sandbox workspace; the agent workspace stays hidden. |
workspaceAccess: "ro" | Exposes the agent workspace as read-only at /agent. |
workspaceAccess: "rw" | Exposes the agent workspace as read/write at /workspace. |
docker.binds entry :ro/:rw | Affects only that extra host folder at its designated container path. |
Adjusting workspaceAccess has no effect on an additional bind between ro and rw, or the reverse. Global and per-agent docker.binds settings combine. For per-agent binds, stick with scope: "agent" or "session"; scope: "shared" disregards all per-agent Docker overrides and applies only global binds.
Bind mounts represent the supported multi-folder limit because Docker builds the container's filesystem view with mount isolation, and the ro/rw mode applies to every process inside the sandbox. This limit covers exec, filesystem tools, child processes, and libraries without requiring path-authorization checks in every OpenClaw code path. A host-side path allowlist falls short of this complete boundary when an allowed shell or dependency can reach files directly.
The opt-in dangerouslyAllowExternalBindSources only permits sources outside the workspace roots. It does not turn off OpenClaw's blocked system, credential, Docker socket, symlink-parent, or reserved-target checks. Choose the smallest folder, use ro unless writes are necessary, and rebuild the sandbox after any mount changes:
openclaw sandbox recreate --agent research
Other bind behavior
agents.defaults.sandbox.docker.binds sets up global mounts. The format matches the host:container:mode form (for instance, "/home/user/source:/source:rw").
agents.defaults.sandbox.browser.binds mounts extra host directories into the sandbox browser container exclusively. When configured (including []), it overrides docker.binds for the browser container; when absent, the browser container defaults to docker.binds.
{
agents: {
defaults: {
sandbox: {
docker: {
binds: ["/home/user/source:/source:ro", "/var/data/myapp:/data:ro"],
},
},
},
entries: {
build: {
default: true,
sandbox: {
docker: {
binds: ["/mnt/cache:/cache:rw"],
},
},
},
},
},
}
Warning
Bind security
- Bind mounts grant direct access to host paths, bypassing the sandbox filesystem, with the permissions you specify via
:roor:rw.- By default, OpenClaw rejects dangerous bind sources: system directories (
/etc,/proc,/sys,/dev,/root,/boot), Docker socket folders (/run,/var/run, and theirdocker.sockequivalents), and standard home-directory credential locations (~/.aws,~/.cargo,~/.config,~/.docker,~/.gnupg,~/.netrc,~/.npm,~/.ssh).- The validation routine normalizes the source path, then re-resolves it through the nearest existing ancestor before checking blocked paths and allowed roots again. This ensures symlink-parent escapes fail closed even if the final leaf does not yet exist; for instance,
/workspace/run-link/new-filestill resolves to/var/run/...whenrun-linkpoints there.- Bind targets that overlap the reserved container mount points (
/workspace,/agent) are also disallowed by default; you can override this withagents.defaults.sandbox.docker.dangerouslyAllowReservedContainerTargets: true.- Bind sources outside the workspace/agent-workspace allowlisted roots are blocked unless overridden with
agents.defaults.sandbox.docker.dangerouslyAllowExternalBindSources: true. Allowed roots undergo the same canonicalization, so a path that appears inside the allowlist only before symlink resolution is still treated as outside the allowed roots.- Sensitive mounts, such as secrets, SSH keys, or service credentials, should use
:rounless they are strictly necessary.- Pair this with
workspaceAccess: "ro"when read-only workspace access suffices; bind modes remain independent.- Refer to Sandbox vs Tool Policy vs Elevated for how binds interact with tool policy and elevated exec.
Images and setup
Default Docker image: openclaw-sandbox:bookworm-slim
Note
Source checkout vs npm install
The
scripts/sandbox-setup.sh,scripts/sandbox-common-setup.sh, andscripts/sandbox-browser-setup.shhelper scripts appear only when you run from a source checkout. They are absent from the npm package.For a global OpenClaw npm installation, rely on the inline
docker buildcommands shown below.
Build the default image
Running from a source checkout:
scripts/sandbox-setup.sh
Running from an npm install (no source checkout required):
docker build -t openclaw-sandbox:bookworm-slim - <<'DOCKERFILE'
FROM debian:bookworm-slim
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
bash ca-certificates curl git jq python3 ripgrep \
&& rm -rf /var/lib/apt/lists/*
RUN useradd --create-home --shell /bin/bash sandbox
USER sandbox
WORKDIR /home/sandbox
CMD ["sleep", "infinity"]
DOCKERFILE
Node is not part of the default image. If a skill depends on Node or other runtimes, either create a custom image or install it via sandbox.docker.setupCommand, which needs network egress, a writable root, and a root user.
OpenClaw will not quietly substitute plain debian:bookworm-slim when openclaw-sandbox:bookworm-slim is missing. Sandbox runs targeting the default image fail immediately with a build instruction until you build it, because the bundled image includes python3 for the sandbox write/edit helpers.
Optional: build the common image
For a sandbox image with more tooling, such as curl, jq, Node 24, pnpm, python3, and git:
From a source checkout:
scripts/sandbox-common-setup.sh
From an npm install, first build the default image (see above), then layer the common image on top using scripts/docker/sandbox/Dockerfile.common from the repository.
After that, set agents.defaults.sandbox.docker.image to openclaw-sandbox-common:bookworm-slim.
Optional: build the sandbox browser image
From a source checkout:
scripts/sandbox-browser-setup.sh
The npm package lacks the browser Dockerfile and entrypoint. Build this image from a source checkout.
Local container sandboxes start with no network by default. Change this with agents.defaults.sandbox.docker.network.
The secret egress proxy, which is off by default, only works for Gateway-loopback. Sandbox exec gets neither its proxy/CA environment nor protected sentinels. Sandbox/container proxy reachability is unsupported; do not turn on sandbox networking expecting secret substitution in this release.
Note
Installing packages or altering certificate stores counts as image provisioning, not standard sandbox-turn behavior. The defaults intentionally combine no network, a read-only root filesystem, and a non-root image user, so an in-turn package install should fail. Use a custom image that already includes packages and private certificate roots. If a Node process requires a private CA, also set the CA path for Node, for instance with
NODE_EXTRA_CA_CERTS, through the custom image orsandbox.docker.env.
Sandbox browser Chromium defaults
The sandbox browser image that ships with the gateway applies conservative Chromium startup flags, tuned 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=newapplies whenbrowser.headlessis turned on.--no-sandbox --disable-setuid-sandboxis forced on inside the sandbox browser container.--disable-3d-apis,--disable-gpu, and--disable-software-rasterizerare set by default; these graphics-hardening flags support containers lacking GPU access. For WebGL or other 3D capabilities, setOPENCLAW_BROWSER_DISABLE_GRAPHICS_FLAGS=0.--disable-extensionsis the default; switch toOPENCLAW_BROWSER_DISABLE_EXTENSIONS=0for workflows that depend on extensions.--renderer-process-limit=2is the default;OPENCLAW_BROWSER_RENDERER_PROCESS_LIMIT=<N>governs it, with0preserving Chromium's original behavior.
For a different runtime profile, supply a custom browser image with your own entrypoint. When running Chromium locally (not in a container), use browser.extraArgs to add extra startup flags.
Network security defaults
network: "host"is denied.network: "container:<id>"is denied unless explicitly enabled (avoids namespace join bypass risk).- Break-glass override:
agents.defaults.sandbox.docker.dangerouslyAllowContainerNamespaceJoin: true.
Docker installs and the containerized gateway are documented here: Docker
In Docker gateway deployments, scripts/docker/setup.sh can initialize sandbox configuration. Enable that path by setting OPENCLAW_SANDBOX=1 (or true/yes/on). Override the socket location via OPENCLAW_DOCKER_SOCKET. For complete setup and environment reference, see Docker.
setupCommand (one-time container setup)
setupCommand executes once, right after the sandbox container is created, not on subsequent runs. It runs inside the container through sh -lc.
Paths:
- Global:
agents.defaults.sandbox.docker.setupCommand - Per-agent:
agents.entries.*.sandbox.docker.setupCommand
Common pitfalls
- The default
docker.networkis"none"(no egress), so package installation fails. docker.network: "container:<id>"is break-glass only and requiresdangerouslyAllowContainerNamespaceJoin: true.readOnlyRoot: trueblocks writes; setreadOnlyRoot: falseor build a custom image instead.- Package installs require
userto be root. Docker can skipuseror setuser: "0:0"; rootful Podman must setuser: "0:0"because its default preserves workspace ownership. Rootless Podman rejects zero-valued users; bake packages into the image or switch to rootful Podman. - Sandbox exec does not inherit host
process.env. For skill API keys, useagents.defaults.sandbox.docker.env(or a custom image). - Values in
agents.defaults.sandbox.docker.envare passed as explicit container environment variables. Anyone with access to the chosen container engine can view them via metadata commands likedocker inspectorpodman inspect. If that metadata exposure is unacceptable, use a custom image, a mounted secret file, or another secret delivery path.
Tool policy and escape hatches
Tool allow/deny policies take effect before sandbox rules are evaluated. A tool that is denied globally or for a specific agent cannot be restored through sandboxing.
tools.elevated serves as an explicit escape hatch, executing exec outside the sandbox (gateway by default, or node when the exec target is node). /exec directives apply only to authorized senders and remain active for the duration of the session; to fully disable exec, apply a tool policy deny (refer to Sandbox vs Tool Policy vs Elevated).
Troubleshooting:
openclaw sandbox listlists sandbox containers along with their status, image match, age, idle time, and linked session or agent.openclaw sandbox explain [--session <key>] [--agent <id>]examines the effective sandbox mode, host workspace, runtime workdir, Docker mounts, tool policy, and fix-it configuration keys. ItsworkspaceRootfield retains the configured sandbox root, whileeffectiveHostWorkspaceRootindicates the actual location of the active workspace.openclaw sandbox recreate [--all | --session <key> | --agent <id>] [--browser] [--force]deletes containers or environments, forcing them to be recreated with the current configuration on the next use.- For the "why is this blocked?" reasoning, see Sandbox vs Tool Policy vs Elevated.
Multi-agent overrides
Each agent has the ability to override sandbox and tool settings: agents.entries.*.sandbox and agents.entries.*.tools (along with agents.entries.*.tools.sandbox.tools for the sandbox tool policy). Precedence details are covered in Multi-Agent Sandbox & Tools.
Minimal enable example
{
agents: {
defaults: {
sandbox: {
mode: "non-main",
scope: "session",
workspaceAccess: "none",
},
},
},
}
Related
- Multi-Agent Sandbox & Tools -- per-agent overrides and their precedence
- OpenShell -- managed sandbox backend setup, workspace modes, and configuration reference
- Sandbox configuration
- Sandbox vs Tool Policy vs Elevated -- troubleshooting "why is this blocked?"
- Security