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 (gateway by default, or node when the exec target is node). With sandboxing disabled, tools.elevated has 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:

SettingKeyValuesDefault
Modeagents.defaults.sandbox.modeoff, non-main, alloff
Scopeagents.defaults.sandbox.scopeagent, session, sharedagent
Backendagents.defaults.sandbox.backenddocker, podman, ssh, openshelldocker

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 at agent:<agentId>:main (or global when session.scope is 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-agent docker/ssh/browser overrides 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 backendSSHOpenShell
Where it runsLocal Docker or Podman containerAny SSH-accessible hostOpenShell managed sandbox
SetupDocker and/or PodmanSSH 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 sandboxDocker engine onlyNot supportedNot supported yet
Bind mountsdocker.bindsN/AN/A
Best forLocal development and container isolationOffloading to a remote machineManaged 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.

CapabilityDockerSSHOpenShell
Shell and child processesSupported inside the containerSupported on the remote hostSupported inside the managed sandbox
File toolsSupported through the container filesystem bridgeSupported through the SSH filesystem bridgeSupported through the SSH bridge in mirror or remote mode
Workspace accessnone, ro, and rwnone, ro, and rwnone, ro, and rw
Network restrictiondocker.network; defaults to "none"Controlled by the remote hostControlled by the selected OpenShell policy
Sandboxed browserSupported in a separate browser containerNot supportedNot supported
Additional host foldersdocker.binds with explicit :ro or :rwNot supported as mounts; seed or copy files insteadNot supported as mounts; use workspace sync or remote files
Packages and runtimesBake a custom image, or use setupCommand with the required privilegesProvision them on the remote hostInclude them in the source image or install when policy permits
Private certificate rootsBake or mount them into the image and configure the consuming runtimeConfigure the remote host trust storeInclude them in the source image or configure them inside sandbox
Plugin and MCP tool accessGateway-side execution, additionally gated by sandbox tool policyGateway-side execution, additionally gated by policyGateway-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.json workspace must 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 workspace path. 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 as EACCES when 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_exec and sandbox_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-write shell 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 denied and bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted. Run openclaw 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=0 is 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 to true) and autoStartTimeoutMs (defaulting to 12s).
  • Sandbox browser containers operate on a dedicated Docker network called openclaw-sandbox-browser, not the global bridge network. Set this up via agents.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 --fix turns 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 instance 172.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 to false).
  • Optional allowlists control access to target: "custom" via allowedControlUrls, allowedControlHosts, and allowedControlPorts.

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.enabled off, or install Docker and choose backend: "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 tmpfs or 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 workspace to 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 HOME at that path and direct OPENCLAW_HOME, OPENCLAW_STATE_DIR, and OPENCLAW_CONFIG_DIR to 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 by identityData/certificateData/knownHostsData, resolved via the standard secrets runtime snapshot, written to temporary files with mode 0600, and removed once the SSH session ends. When both a *File and a *Data variant are configured for the same item, *Data takes 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 recreate does. This backend does not support browser sandboxing, and sandbox.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:

ValueBehavior
none (default)Tools are presented with an isolated sandbox workspace at ~/.openclaw/sandboxes.
roThe agent workspace is mounted read-only at /agent (write/edit/apply_patch are disabled).
rwThe 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 read tool is rooted in the sandbox. With workspaceAccess: "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.
  • rw allows 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:

SettingControls
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/:rwAffects 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 :ro or :rw.
  • By default, OpenClaw rejects dangerous bind sources: system directories (/etc, /proc, /sys, /dev, /root, /boot), Docker socket folders (/run, /var/run, and their docker.sock equivalents), 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-file still resolves to /var/run/... when run-link points there.
  • Bind targets that overlap the reserved container mount points (/workspace, /agent) are also disallowed by default; you can override this with agents.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 :ro unless 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, and scripts/sandbox-browser-setup.sh helper 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 build commands 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 or sandbox.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=new applies when browser.headless is turned on.
  • --no-sandbox --disable-setuid-sandbox is forced on inside the sandbox browser container.
  • --disable-3d-apis, --disable-gpu, and --disable-software-rasterizer are set by default; these graphics-hardening flags support containers lacking GPU access. For WebGL or other 3D capabilities, set OPENCLAW_BROWSER_DISABLE_GRAPHICS_FLAGS=0.
  • --disable-extensions is the default; switch to OPENCLAW_BROWSER_DISABLE_EXTENSIONS=0 for workflows that depend on extensions.
  • --renderer-process-limit=2 is the default; OPENCLAW_BROWSER_RENDERER_PROCESS_LIMIT=<N> governs it, with 0 preserving 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.network is "none" (no egress), so package installation fails.
  • docker.network: "container:<id>" is break-glass only and requires dangerouslyAllowContainerNamespaceJoin: true.
  • readOnlyRoot: true blocks writes; set readOnlyRoot: false or build a custom image instead.
  • Package installs require user to be root. Docker can skip user or set user: "0:0"; rootful Podman must set user: "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, use agents.defaults.sandbox.docker.env (or a custom image).
  • Values in agents.defaults.sandbox.docker.env are passed as explicit container environment variables. Anyone with access to the chosen container engine can view them via metadata commands like docker inspect or podman 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 list lists 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. Its workspaceRoot field retains the configured sandbox root, while effectiveHostWorkspaceRoot indicates 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",
      },
    },
  },
}
4,624 words · updated Aug 25, 2026