On this page

On this page

Gateway

Docker backend

The default local backend: its restricted defaults, GPU and Docker-out-of-Docker constraints, and the sandboxed browser container.

Docker backend

The Docker backend runs tools locally through the docker CLI. Its selection and error behavior are unchanged; it does not check or fall back to Podman.

Defaults: network: "none" (no egress), readOnlyRoot: true, capDrop: ["ALL"], image openclaw-sandbox:trixie-slim.

Tmpfs mounts without an explicit size= option are capped at 1 GiB for /tmp, 4 GiB for the managed dependency store at /tmp/openclaw-deps, and 256 MiB for other paths. Existing explicit sizes are preserved. These are capacity limits, not reserved memory; writes beyond a mount's limit fail with no space left. Keep durable output in the disk-backed workspace. Dependency builders stop after setup, releasing their temporary HOME, caches, and pnpm store; prepared node_modules remains in the workspace template. Runtime containers use the same pnpm store path so warm dependencies remain reusable without a reinstall. Podman's automatic writable /tmp, /var/tmp, and /run mounts receive these same caps, while its nested init mount remains available.

This explicit configuration keeps the agent workspace read-only and preserves the default restricted runtime posture:

json5
{  agents: {    defaults: {      sandbox: {        mode: "all",        backend: "docker",        scope: "session",        workspaceAccess: "ro",        docker: {          image: "openclaw-sandbox:trixie-slim",          readOnlyRoot: true,          tmpfs: ["/tmp", "/var/tmp", "/run"],          network: "none",          capDrop: ["ALL"],        },      },    },  },}

OpenClaw also creates Docker sandbox containers with an init process and no-new-privileges. With workspaceAccess: "ro", the agent workspace is mounted read-only at /agent; write operations to the agent workspace are rejected, while the configured tmpfs paths remain writable.

File tools require a host-backed bind mount. A tmpfs or image volume can hide files below a workspace bind; file tools report those paths as container-only instead of reading the hidden host files. Use exec to access that storage. A deeper explicit bind restores file-tool access when it is visible in the container. Mount destinations reached through symlinks, or stacked mounts with different backing storage, also require exec when their host projection cannot be established from the container's mount table.

For recently used containers, changes to bind sources or access modes, or to tmpfs destinations or read-only modes below a bind, require scoped recreation. Other tmpfs options, such as size, mode, and uid, keep the normal configuration change behavior: a hot container stays running with a recreation notice, while a stopped or expired container is replaced. Recreate explicitly to apply those options immediately.

After a Gateway restart, Docker and Podman tool containers can be replaced on their first use when an upgrade adds Gateway-managed read-only skill or resource mounts. Every existing mount must still match; replaced sources do not qualify. The old container must have a read-only root, no image volumes, and no retained persistent-exec custody; its last successful use must predate this Gateway process. Workspace and user bind sources and access modes must still match. Workspace files remain on the host, but replacement discards temporary container state and untracked background processes. Writable roots, user mount changes, restricted dispatch requiring current config, and hot browser containers still require explicit recreation.

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 passed to the selected container engine's Docker-compatible --gpus flag and requires compatible host GPU setup. Podman requires version 5.0 or newer for this option.

Idle containers

Docker and Podman tool containers stop after 15 minutes without a new sandbox admission, on the Gateway's five-minute housekeeping sweep. Active turns, retained background commands, and persistent executions prevent idle stopping. Each pass attempts at most 32 idle stops, sequentially; failed attempts count toward the limit. Each sweep resumes after the last attempted container, so repeated failures cannot starve later containers. Backlogs drain over multiple sweeps. Dependency-builder cleanup can request earlier sweeps. Existing age-based removal is separate from this idle-stop limit. A long turn becomes eligible for stopping as soon as it finishes. Stopping frees tmpfs contents and ends untracked background processes; workspace bind mounts and the container's writable layer persist. The next turn starts the same container without rerunning one-time setup. Prepared dependency templates remain available for warm reuse. The existing sandbox.prune policy separately removes old containers; disabling removal does not disable idle stopping.

Container creation-policy changes are checked on the next use of each sandbox, not by recreating the inventory at startup. A cold container may be replaced on that first use; its host workspace and prepared dependency templates persist.

Dependency installation that exhausts storage fails preparation with an actionable error rather than publishing a partial dependency template. Check free bytes and inodes in the workspace and /tmp/openclaw-deps. For a full temporary pnpm store, the existing sandbox.docker.env.PNPM_CONFIG_STORE_DIR setting can place the store on a disk-backed path inside the workspace before retrying.

Container user

When agents.defaults.sandbox.docker.user is unset, Docker uses the mounted workspace owner's numeric uid:gid: the agent workspace for workspaceAccess: "rw", otherwise the sandbox workspace. A root-owned workspace can therefore run as root; if ownership cannot be read, the image's default user applies. Set docker.user explicitly to override this. setupCommand runs as the same container user. See Podman backend for its user-namespace and root-user restrictions.

Shell environment

Sandboxed exec commands default HOME to the sandbox workspace root, even when workdir selects a task subdirectory. Home-based configuration therefore persists across commands in that workspace. Explicit sandbox.docker.env or command env values can override HOME and XDG paths. The host user's home and package stores are not mounted or inherited by this default; separate sandbox workspaces keep separate stores.

Managed task workspaces default XDG, npm, pip, uv, and Bun caches to /tmp/openclaw-deps/cache; pnpm uses /tmp/openclaw-deps/data/pnpm/store. These share the existing 4 GiB dependency tmpfs limit, not separate quotas. Explicit sandbox and command environment settings still override these defaults. Cache exhaustion reports no space left; remove disposable cache contents or use an explicitly mounted, non-projected disk path through the existing environment settings. Increasing a workspace result limit does not fix cache exhaustion.

Workspace capture and transfer omit exact tool-owned cache roots left by older sandboxes: .npm/_cacache, .npm/_logs, .cache/pip, .cache/uv, .pnpm-store, .bun/install/cache, .cargo/registry, .cargo/git, and .gradle/caches. Cargo and Gradle retain their workspace configuration; their cache subdirectories use this exclusion policy. Files elsewhere in .npm, .cache, .cargo, and .gradle, nested project fixtures, and staged inputs remain workspace data. Caches already on disk are not deleted. Large user files still hit the result size guard; a recognized cache in a nested project names its cache class and the environment setting for moving it outside the workspace before recovery.

Docker daemon

The Docker CLI inherits the Gateway process environment except MALLOC_CONF and jemalloc entries in LD_PRELOAD. Docker and Podman host launches remove those Gateway allocator settings so their subprocesses do not inherit background threads or profiling; other preload libraries are preserved. Choose the daemon with DOCKER_HOST or DOCKER_CONTEXT (set only one), or the CLI's current context; there is no sandbox.docker daemon-selection config key. For rootless Docker, set DOCKER_HOST=unix:///run/user/<uid>/docker.sock in the Gateway service environment, then restart the Gateway. This differs from sandbox.docker.env, which configures the container environment.

For local Unix sockets, OpenClaw records the endpoint and daemon ID per runtime and refuses reuse if the selected daemon changes. Restore the original connection or recreate the runtime while its recorded daemon is reachable.

Sandboxed browser

  • The sandbox browser auto-starts and waits for CDP to become reachable when the browser tool needs it. Configure via agents.defaults.sandbox.browser.autoStart (default true) and autoStartTimeoutMs (default 12s).
  • Sandbox browser containers use a dedicated Docker network (openclaw-sandbox-browser) instead of the global bridge network. Configure with agents.defaults.sandbox.browser.network.
  • Sandbox browser network mode "none" is unsupported because browser control requires host-published CDP ports. Use the dedicated default, bridge, or another custom bridge network. openclaw doctor --fix disables affected persisted sidecars and restores the dedicated network without silently enabling egress.
  • 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 the password in the URL fragment (not query string or header logs).
  • agents.defaults.sandbox.browser.allowHostControl (default false) lets sandboxed sessions target the host browser explicitly.
  • Optional allowlists gate target: "custom": allowedControlUrls, allowedControlHosts, allowedControlPorts.
Was this useful?