Skip to content

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.

  • Tool execution (exec, read, write, edit, apply_patch, process, etc.).
  • Optional sandboxed browser (agents.defaults.sandbox.browser).
Sandboxed browser details
  • 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.
  • 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).
  • 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.allowHostControl lets sandboxed sessions target the host browser explicitly.
  • Optional allowlists gate target: "custom": allowedControlUrls, allowedControlHosts, allowedControlPorts.

Not sandboxed:

  • The Gateway process itself.
  • Any tool explicitly allowed to run outside the sandbox (e.g. tools.elevated).
    • Elevated exec bypasses sandboxing and uses the configured escape path (gateway by default, or node when the exec target is node).
    • If sandboxing is off, tools.elevated does not change execution (already on host). See Elevated Mode.

agents.defaults.sandbox.mode controls when sandboxing is used:

No sandboxing.

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.

DockerSSHOpenShell
Where it runsLocal containerAny SSH-accessible hostOpenShell managed sandbox
Setupscripts/sandbox-setup.shSSH key + target hostOpenShell plugin enabled
Workspace modelBind-mount or copyRemote-canonical (seed once)mirror or remote
Network controldocker.network (default: none)Depends on remote hostDepends on OpenShell
Browser sandboxSupportedNot supportedNot supported yet
Bind mountsdocker.bindsN/AN/A
Best forLocal dev, full isolationOffloading to a remote machineManaged 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.

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" },
},
},
},
},
}
How it works
  • 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.
Authentication material
  • 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 with 0600, and deletes them when the SSH session ends.
  • If both *File and *Data are set for the same item, *Data wins for that SSH session.
Remote-canonical consequences

This is a remote-canonical model. 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 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.
Remote transport details
  • OpenClaw asks OpenShell for sandbox-specific SSH config via `openshell sandbox ssh-config

. - 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 mirror` mode only the lifecycle differs: sync local to remote before exec, then sync back after exec.

Current OpenShell limitations
  • sandbox browser is not supported yet
  • sandbox.docker.binds is not supported on the OpenShell backend
  • Docker-specific runtime knobs under sandbox.docker.* still apply only to the Docker backend

OpenShell has two workspace models. This is the part that matters most in practice.

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.

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 runtimes
  • openclaw sandbox recreate deletes the current runtime and lets OpenClaw recreate it on next use
  • prune logic is backend-aware too

For 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

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:

Tools see a sandbox workspace under ~/.openclaw/sandboxes.

With the OpenShell backend:

  • mirror mode still uses the local workspace as the canonical source between exec turns
  • remote mode uses the remote OpenShell workspace as the canonical source after the initial seed
  • workspaceAccess: "ro" and "none" still restrict write behavior the same way

Inbound 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.

  • 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).

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"],
},
},
},
],
},
}

Default Docker image: openclaw-sandbox:bookworm-slim

  1. Build the default image

    From a source checkout:

    Terminal window
    scripts/sandbox-setup.sh

    From an npm install (no source checkout needed):

    Terminal window
    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.

  2. Optional: build the common image

    For a more functional sandbox image with common tooling (for example curl, jq, nodejs, python3, git):

    From a source checkout:

    Terminal window
    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 from the repository.

    Then set agents.defaults.sandbox.docker.image to openclaw-sandbox-common:bookworm-slim.

  3. Optional: build the sandbox browser image

    From a source checkout:

    Terminal window
    scripts/sandbox-browser-setup.sh

    From an npm install, build using the scripts/docker/sandbox/Dockerfile.browser from the repository.

By default, Docker sandbox containers run with no network. Override with agents.defaults.sandbox.docker.network.

Sandbox browser Chromium defaults

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=

-—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-sandboxwhennoSandbox 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=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=

, 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.
Network security defaults
  • network: "host" is blocked.
  • `network: “container:

is blocked by default (namespace join bypass risk). - Break-glass override:agents.defaults.sandbox.docker.dangerouslyAllowContainerNamespaceJoin: true`.

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:

  • Global: agents.defaults.sandbox.docker.setupCommand
  • Per-agent: agents.list[].sandbox.docker.setupCommand
Common pitfalls
  • Default docker.network is "none" (no egress), so package installs will fail.
  • `docker.network: “container:

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. Use agents.defaults.sandbox.docker.env(or a custom image) for skill API keys. - Values inagents.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 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:

  • Use openclaw sandbox explain to 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.

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",
},
},
},
}