docs/gateway/sandboxing.md
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.
exec, read, write, edit, apply_patch, process, etc.).agents.defaults.sandbox.browser).Not sandboxed:
tools.elevated).
gateway by default, or node when the exec target is node).tools.elevated does not change execution (already on host). See Elevated Mode.agents.defaults.sandbox.mode controls when sandboxing is used:
`"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.
agents.defaults.sandbox.scope controls how many containers are created:
"agent" (default): one container per agent."session": one container per session."shared": one container shared by all sandboxed sessions.agents.defaults.sandbox.backend controls which runtime provides the sandbox:
"docker" (default when sandboxing is enabled): local Docker-backed sandbox runtime."ssh": generic SSH-backed remote sandbox runtime."openshell": OpenShell-backed sandbox runtime.SSH-specific config lives under 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 |
Sandboxing is off by default. If you enable sandboxing and do not choose a backend, OpenClaw uses the Docker backend. It executes tools and sandbox browsers locally via the Docker daemon socket (/var/run/docker.sock). Sandbox container isolation is determined by Docker namespaces.
To expose host GPUs to Docker sandboxes, set agents.defaults.sandbox.docker.gpus or the per-agent agents.list[].sandbox.docker.gpus override. The value is passed to Docker's --gpus flag as a separate argument, for example "all" or "device=GPU-uuid", and requires a compatible host runtime such as NVIDIA Container Toolkit.
If you deploy the OpenClaw Gateway itself as a Docker container, it orchestrates sibling sandbox containers using the host's Docker socket (DooD). This introduces a specific path mapping constraint:
openclaw.json workspace configuration MUST contain the Host's absolute path (e.g. /home/user/.openclaw/workspaces), not the internal Gateway container path. When OpenClaw asks the Docker daemon to spawn a sandbox, the daemon evaluates paths relative to the Host OS namespace, not the Gateway namespace.workspace directory. Because the Gateway evaluates the exact same string (the host path) from within its own containerized environment, the Gateway deployment MUST include an identical volume map linking the host namespace natively (-v /home/user/.openclaw:/home/user/.openclaw).If you map paths internally without absolute host parity, OpenClaw natively throws an EACCES permission error attempting to write its heartbeat inside the container environment because the fully qualified path string doesn't exist natively.
</Warning>
Use backend: "ssh" when you want OpenClaw to sandbox exec, file tools, and media reads on an arbitrary 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" },
},
},
},
},
}
- Host-local edits made outside OpenClaw after the seed step are not visible remotely until you recreate the sandbox.
- `openclaw sandbox recreate` deletes 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.
Use backend: "openshell" when you want OpenClaw to sandbox tools in an OpenShell-managed remote environment. For the full setup guide, configuration reference, and workspace mode comparison, see the dedicated OpenShell page.
OpenShell reuses the same core SSH transport and remote filesystem bridge as the generic SSH backend, and adds OpenShell-specific lifecycle (sandbox create/get/delete, sandbox ssh-config) plus the optional mirror workspace mode.
{
agents: {
defaults: {
sandbox: {
mode: "all",
backend: "openshell",
scope: "session",
workspaceAccess: "rw",
},
},
},
plugins: {
entries: {
openshell: {
enabled: true,
config: {
from: "openclaw",
mode: "remote", // mirror | remote
remoteWorkspaceDir: "/sandbox",
remoteAgentWorkspaceDir: "/agent",
},
},
},
},
}
OpenShell modes:
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.OpenShell has two workspace models. This is the part that matters most in practice.
<Tabs> <Tab title="mirror (local canonical)"> 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.
Use this when:
- 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
Tradeoff: extra sync cost before and after exec.
Behavior:
- When the sandbox is first created, OpenClaw seeds the remote workspace from the local workspace once.
- After that, `exec`, `read`, `write`, `edit`, and `apply_patch` operate 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`.
Important consequences:
- 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"` or `scope: "shared"`, that remote workspace is shared at that same scope.
Use this when:
- 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
Choose 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 sandboxes are still managed through the normal sandbox lifecycle:
openclaw sandbox list shows OpenShell runtimes as well as Docker runtimesopenclaw sandbox recreate deletes the current runtime and lets OpenClaw recreate it on next useFor remote mode, recreate is especially important:
For mirror mode, recreate mainly resets the remote execution environment because the local workspace remains canonical anyway.
agents.defaults.sandbox.workspaceAccess controls what the sandbox can see:
With the OpenShell backend:
mirror mode still uses the local workspace as the canonical source between exec turnsremote mode uses the remote OpenShell workspace as the canonical source after the initial seedworkspaceAccess: "ro" and "none" still restrict write behavior the same wayInbound media is copied into the active sandbox workspace (media/inbound/*).
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.
[]), it replaces agents.defaults.sandbox.docker.binds for the browser container.agents.defaults.sandbox.docker.binds (backwards compatible).Example (read-only source + an extra data directory):
{
agents: {
defaults: {
sandbox: {
docker: {
binds: ["/home/user/source:/source:ro", "/var/data/myapp:/data:ro"],
},
},
},
list: [
{
id: "build",
sandbox: {
docker: {
binds: ["/mnt/cache:/cache:rw"],
},
},
},
],
},
}
:ro or :rw).docker.sock, /etc, /proc, /sys, /dev, and parent mounts that would expose them).~/.aws, ~/.cargo, ~/.config, ~/.docker, ~/.gnupg, ~/.netrc, ~/.npm, and ~/.ssh./workspace/run-link/new-file still resolves as /var/run/... if run-link points there.outside allowed roots.:ro unless absolutely required.workspaceAccess: "ro" if you only need read access to the workspace; bind modes stay independent.Default Docker image: openclaw-sandbox:bookworm-slim
The 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.
</Note>
```bash
scripts/sandbox-setup.sh
```
From an npm install (no source checkout needed):
```bash
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
```
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 sandbox write/edit helpers.
From a source checkout:
```bash
scripts/sandbox-common-setup.sh
```
From an npm install, build the default image first (see above), then build the common image on top using the [`scripts/docker/sandbox/Dockerfile.common`](https://github.com/openclaw/openclaw/blob/main/scripts/docker/sandbox/Dockerfile.common) from the repository.
Then set `agents.defaults.sandbox.docker.image` to `openclaw-sandbox-common:bookworm-slim`.
```bash
scripts/sandbox-browser-setup.sh
```
From an npm install, build using the [`scripts/docker/sandbox/Dockerfile.browser`](https://github.com/openclaw/openclaw/blob/main/scripts/docker/sandbox/Dockerfile.browser) from the repository.
By default, Docker sandbox containers run with no network. Override with agents.defaults.sandbox.docker.network.
- `--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-sandbox` when `noSandbox` is enabled.
- The three graphics hardening flags (`--disable-3d-apis`, `--disable-software-rasterizer`, `--disable-gpu`) are optional and are useful when containers lack GPU support. Set `OPENCLAW_BROWSER_DISABLE_GRAPHICS_FLAGS=0` if your workload requires WebGL or other 3D/browser features.
- `--disable-extensions` is enabled by default and can be disabled with `OPENCLAW_BROWSER_DISABLE_EXTENSIONS=0` for extension-reliant flows.
- `--renderer-process-limit=2` is controlled by `OPENCLAW_BROWSER_RENDERER_PROCESS_LIMIT=<N>`, where `0` keeps Chromium's default.
If you need a different runtime profile, use a custom browser image and provide your own entrypoint. For local (non-container) Chromium profiles, use `browser.extraArgs` to append additional startup flags.
Docker installs and the containerized gateway live here: Docker
For Docker gateway deployments, scripts/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 runs once after the sandbox container is created (not on every run). It executes inside the container via sh -lc.
Paths:
agents.defaults.sandbox.docker.setupCommandagents.list[].sandbox.docker.setupCommandTool 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 explain to inspect effective sandbox mode, tool policy, and fix-it config keys.Keep it locked down.
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.
{
agents: {
defaults: {
sandbox: {
mode: "non-main",
scope: "session",
workspaceAccess: "none",
},
},
},
}