2026-01-02 20:58:50 +01:00
---
2026-01-31 21:13:13 +09:00
summary: "Optional Docker-based setup and onboarding for OpenClaw"
2026-01-02 20:58:50 +01:00
read_when:
- You want a containerized gateway instead of local installs
- You are validating the Docker flow
2026-01-31 16:04:03 -05:00
title: "Docker"
2026-01-02 20:58:50 +01:00
---
# Docker (optional)
Docker is **optional ** . Use it only if you want a containerized gateway or to validate the Docker flow.
2026-01-08 21:56:41 +01:00
## Is Docker right for me?
2026-01-30 03:15:10 +01:00
- **Yes**: you want an isolated, throwaway gateway environment or to run OpenClaw on a host without local installs.
2026-01-08 21:56:41 +01:00
- **No**: you’ re running on your own machine and just want the fastest dev loop. Use the normal install flow instead.
- **Sandboxing note**: agent sandboxing uses Docker too, but it does **not ** require the full gateway to run in Docker. See [Sandboxing ](/gateway/sandboxing ).
2026-01-03 21:35:44 +01:00
This guide covers:
2026-01-31 18:31:49 +09:00
2026-01-30 03:15:10 +01:00
- Containerized Gateway (full OpenClaw in Docker)
2026-01-03 21:35:44 +01:00
- Per-session Agent Sandbox (host gateway + Docker-isolated agent tools)
2026-01-02 20:58:50 +01:00
2026-01-08 21:49:26 +01:00
Sandboxing details: [Sandboxing ](/gateway/sandboxing )
2026-01-03 21:35:44 +01:00
## Requirements
- Docker Desktop (or Docker Engine) + Docker Compose v2
- Enough disk for images + logs
## Containerized Gateway (Docker Compose)
### Quick start (recommended)
From repo root:
2026-01-02 20:58:50 +01:00
```bash
./docker-setup.sh
```
This script:
2026-01-31 18:31:49 +09:00
2026-01-03 21:35:44 +01:00
- builds the gateway image
2026-01-02 20:58:50 +01:00
- runs the onboarding wizard
2026-01-08 07:48:23 +00:00
- prints optional provider setup hints
2026-01-02 20:58:50 +01:00
- starts the gateway via Docker Compose
2026-01-08 21:56:41 +01:00
- generates a gateway token and writes it to `.env`
2026-01-11 03:27:48 +01:00
Optional env vars:
2026-01-31 18:31:49 +09:00
2026-01-30 03:15:10 +01:00
- `OPENCLAW_DOCKER_APT_PACKAGES` — install extra apt packages during build
- `OPENCLAW_EXTRA_MOUNTS` — add extra host bind mounts
- `OPENCLAW_HOME_VOLUME` — persist `/home/node` in a named volume
2026-01-11 03:27:48 +01:00
2026-01-08 21:56:41 +01:00
After it finishes:
2026-01-31 18:31:49 +09:00
2026-01-08 21:56:41 +01:00
- Open `http://127.0.0.1:18789/` in your browser.
- Paste the token into the Control UI (Settings → token).
2026-02-05 18:08:29 -08:00
- Need the URL again? Run `docker compose run --rm openclaw-cli dashboard --no-open` .
2026-01-02 20:58:50 +01:00
It writes config/workspace on the host:
2026-01-31 18:31:49 +09:00
2026-01-30 03:15:10 +01:00
- `~/.openclaw/`
- `~/.openclaw/workspace`
2026-01-02 20:58:50 +01:00
2026-02-05 17:45:01 -05:00
Running on a VPS? See [Hetzner (Docker VPS) ](/install/hetzner ).
2026-01-09 18:21:28 +01:00
2026-02-10 16:04:41 -05:00
### Shell Helpers (optional)
For easier day-to-day Docker management, install `ClawDock` :
```bash
mkdir -p ~/.clawdock && curl -sL https://raw.githubusercontent.com/openclaw/openclaw/main/scripts/shell-helpers/clawdock-helpers.sh -o ~/.clawdock/clawdock-helpers.sh
```
**Add to your shell config (zsh):**
```bash
echo 'source ~/.clawdock/clawdock-helpers.sh' >> ~/.zshrc && source ~/.zshrc
```
Then use `clawdock-start` , `clawdock-stop` , `clawdock-dashboard` , etc. Run `clawdock-help` for all commands.
See [`ClawDock` Helper README ](https://github.com/openclaw/openclaw/blob/main/scripts/shell-helpers/README.md ) for details.
2026-01-03 21:35:44 +01:00
### Manual flow (compose)
2026-01-02 20:58:50 +01:00
```bash
2026-01-30 03:15:10 +01:00
docker build -t openclaw:local -f Dockerfile .
docker compose run --rm openclaw-cli onboard
docker compose up -d openclaw-gateway
2026-01-02 20:58:50 +01:00
```
2026-02-02 04:25:57 -08:00
Note: run `docker compose ...` from the repo root. If you enabled
`OPENCLAW_EXTRA_MOUNTS` or `OPENCLAW_HOME_VOLUME` , the setup script writes
`docker-compose.extra.yml` ; include it when running Compose elsewhere:
```bash
docker compose -f docker-compose.yml -f docker-compose.extra.yml <command>
```
### Control UI token + pairing (Docker)
If you see “unauthorized” or “disconnected (1008): pairing required”, fetch a
fresh dashboard link and approve the browser device:
```bash
docker compose run --rm openclaw-cli dashboard --no-open
docker compose run --rm openclaw-cli devices list
docker compose run --rm openclaw-cli devices approve <requestId>
```
More detail: [Dashboard ](/web/dashboard ), [Devices ](/cli/devices ).
2026-01-10 20:12:23 +00:00
### Extra mounts (optional)
If you want to mount additional host directories into the containers, set
2026-01-30 03:15:10 +01:00
`OPENCLAW_EXTRA_MOUNTS` before running `docker-setup.sh` . This accepts a
2026-01-10 20:12:23 +00:00
comma-separated list of Docker bind mounts and applies them to both
2026-01-30 03:15:10 +01:00
`openclaw-gateway` and `openclaw-cli` by generating `docker-compose.extra.yml` .
2026-01-10 20:12:23 +00:00
Example:
```bash
2026-01-30 03:15:10 +01:00
export OPENCLAW_EXTRA_MOUNTS="$HOME/.codex:/home/node/.codex:ro,$HOME/github:/home/node/github:rw"
2026-01-10 20:12:23 +00:00
./docker-setup.sh
```
Notes:
2026-01-31 18:31:49 +09:00
2026-01-10 20:12:23 +00:00
- Paths must be shared with Docker Desktop on macOS/Windows.
2026-01-30 03:15:10 +01:00
- If you edit `OPENCLAW_EXTRA_MOUNTS` , rerun `docker-setup.sh` to regenerate the
2026-01-10 20:12:23 +00:00
extra compose file.
2026-01-10 22:42:57 +01:00
- `docker-compose.extra.yml` is generated. Don’ t hand-edit it.
2026-01-10 20:12:23 +00:00
### Persist the entire container home (optional)
If you want `/home/node` to persist across container recreation, set a named
2026-01-30 03:15:10 +01:00
volume via `OPENCLAW_HOME_VOLUME` . This creates a Docker volume and mounts it at
2026-01-10 22:42:57 +01:00
`/home/node` , while keeping the standard config/workspace bind mounts. Use a
named volume here (not a bind path); for bind mounts, use
2026-01-30 03:15:10 +01:00
`OPENCLAW_EXTRA_MOUNTS` .
2026-01-10 20:12:23 +00:00
Example:
```bash
2026-01-30 03:15:10 +01:00
export OPENCLAW_HOME_VOLUME="openclaw_home"
2026-01-10 20:12:23 +00:00
./docker-setup.sh
```
You can combine this with extra mounts:
```bash
2026-01-30 03:15:10 +01:00
export OPENCLAW_HOME_VOLUME="openclaw_home"
export OPENCLAW_EXTRA_MOUNTS="$HOME/.codex:/home/node/.codex:ro,$HOME/github:/home/node/github:rw"
2026-01-10 20:12:23 +00:00
./docker-setup.sh
```
Notes:
2026-01-31 18:31:49 +09:00
2026-01-30 03:15:10 +01:00
- If you change `OPENCLAW_HOME_VOLUME` , rerun `docker-setup.sh` to regenerate the
2026-01-10 20:12:23 +00:00
extra compose file.
- The named volume persists until removed with `docker volume rm <name>` .
2026-01-11 00:06:19 +00:00
### Install extra apt packages (optional)
If you need system packages inside the image (for example, build tools or media
2026-01-30 03:15:10 +01:00
libraries), set `OPENCLAW_DOCKER_APT_PACKAGES` before running `docker-setup.sh` .
2026-01-11 00:06:19 +00:00
This installs the packages during the image build, so they persist even if the
container is deleted.
Example:
```bash
2026-01-30 03:15:10 +01:00
export OPENCLAW_DOCKER_APT_PACKAGES="ffmpeg build-essential"
2026-01-11 00:06:19 +00:00
./docker-setup.sh
```
Notes:
2026-01-31 18:31:49 +09:00
2026-01-11 00:06:19 +00:00
- This accepts a space-separated list of apt package names.
2026-01-30 03:15:10 +01:00
- If you change `OPENCLAW_DOCKER_APT_PACKAGES` , rerun `docker-setup.sh` to rebuild
2026-01-11 00:06:19 +00:00
the image.
2026-02-02 02:07:00 -08:00
### Power-user / full-featured container (opt-in)
The default Docker image is **security-first ** and runs as the non-root `node`
user. This keeps the attack surface small, but it means:
- no system package installs at runtime
- no Homebrew by default
- no bundled Chromium/Playwright browsers
If you want a more full-featured container, use these opt-in knobs:
2026-02-02 02:19:18 -08:00
1. **Persist `/home/node` ** so browser downloads and tool caches survive:
2026-02-02 02:07:00 -08:00
```bash
export OPENCLAW_HOME_VOLUME="openclaw_home"
./docker-setup.sh
```
2026-02-06 10:00:08 -05:00
2. **Bake system deps into the image ** (repeatable + persistent):
2026-02-02 02:07:00 -08:00
```bash
export OPENCLAW_DOCKER_APT_PACKAGES="git curl jq"
./docker-setup.sh
```
2026-02-06 10:00:08 -05:00
3. **Install Playwright browsers without `npx` ** (avoids npm override conflicts):
2026-02-02 02:07:00 -08:00
```bash
docker compose run --rm openclaw-cli \
node /app/node_modules/playwright-core/cli.js install chromium
```
If you need Playwright to install system deps, rebuild the image with
`OPENCLAW_DOCKER_APT_PACKAGES` instead of using `--with-deps` at runtime.
2026-02-06 10:00:08 -05:00
4. **Persist Playwright browser downloads ** :
2026-02-02 02:07:00 -08:00
- Set `PLAYWRIGHT_BROWSERS_PATH=/home/node/.cache/ms-playwright` in
`docker-compose.yml` .
- Ensure `/home/node` persists via `OPENCLAW_HOME_VOLUME` , or mount
`/home/node/.cache/ms-playwright` via `OPENCLAW_EXTRA_MOUNTS` .
### Permissions + EACCES
The image runs as `node` (uid 1000). If you see permission errors on
`/home/node/.openclaw` , make sure your host bind mounts are owned by uid 1000.
Example (Linux host):
```bash
sudo chown -R 1000:1000 /path/to/openclaw-config /path/to/openclaw-workspace
```
If you choose to run as root for convenience, you accept the security tradeoff.
2026-01-09 15:23:06 -05:00
### Faster rebuilds (recommended)
To speed up rebuilds, order your Dockerfile so dependency layers are cached.
This avoids re-running `pnpm install` unless lockfiles change:
```dockerfile
FROM node:22-bookworm
# Install Bun (required for build scripts)
RUN curl -fsSL https://bun.sh/install | bash
ENV PATH="/root/.bun/bin:${PATH}"
RUN corepack enable
WORKDIR /app
# Cache dependencies unless package metadata changes
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
COPY ui/package.json ./ui/package.json
COPY scripts ./scripts
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
RUN pnpm ui:install
RUN pnpm ui:build
ENV NODE_ENV=production
2026-01-31 18:31:49 +09:00
CMD ["node","dist/index.js"]
2026-01-09 15:23:06 -05:00
```
2026-01-13 06:51:20 +00:00
### Channel setup (optional)
2026-01-08 07:48:23 +00:00
2026-01-13 06:51:20 +00:00
Use the CLI container to configure channels, then restart the gateway if needed.
2026-01-08 07:48:23 +00:00
WhatsApp (QR):
2026-01-31 18:31:49 +09:00
2026-01-08 07:48:23 +00:00
```bash
2026-01-30 03:15:10 +01:00
docker compose run --rm openclaw-cli channels login
2026-01-08 07:48:23 +00:00
```
Telegram (bot token):
2026-01-31 18:31:49 +09:00
2026-01-08 07:48:23 +00:00
```bash
2026-01-30 03:15:10 +01:00
docker compose run --rm openclaw-cli channels add --channel telegram --token "<token>"
2026-01-08 07:48:23 +00:00
```
Discord (bot token):
2026-01-31 18:31:49 +09:00
2026-01-08 07:48:23 +00:00
```bash
2026-01-30 03:15:10 +01:00
docker compose run --rm openclaw-cli channels add --channel discord --token "<token>"
2026-01-08 07:48:23 +00:00
```
2026-01-13 06:16:43 +00:00
Docs: [WhatsApp ](/channels/whatsapp ), [Telegram ](/channels/telegram ), [Discord ](/channels/discord )
2026-01-08 07:48:23 +00:00
2026-02-02 04:25:57 -08:00
### OpenAI Codex OAuth (headless Docker)
If you pick OpenAI Codex OAuth in the wizard, it opens a browser URL and tries
to capture a callback on `http://127.0.0.1:1455/auth/callback` . In Docker or
headless setups that callback can show a browser error. Copy the full redirect
URL you land on and paste it back into the wizard to finish auth.
2026-01-03 21:35:44 +01:00
### Health check
```bash
2026-01-31 18:31:49 +09:00
docker compose exec openclaw-gateway node dist/index.js health --token "$OPENCLAW_GATEWAY_TOKEN"
2026-01-03 21:35:44 +01:00
```
### E2E smoke test (Docker)
2026-01-02 20:58:50 +01:00
```bash
scripts/e2e/onboard-docker.sh
```
2026-01-06 02:22:20 +01:00
### QR import smoke test (Docker)
```bash
pnpm test:docker:qr
```
2026-01-03 21:35:44 +01:00
### Notes
2026-01-02 20:58:50 +01:00
- Gateway bind defaults to `lan` for container use.
2026-02-02 17:17:11 +05:30
- Dockerfile CMD uses `--allow-unconfigured` ; mounted config with `gateway.mode` not `local` will still start. Override CMD to enforce the guard.
2026-01-30 03:15:10 +01:00
- The gateway container is the source of truth for sessions (`~/.openclaw/agents/<agentId>/sessions/` ).
2026-01-03 21:35:44 +01:00
2026-01-07 02:31:51 +01:00
## Agent Sandbox (host gateway + Docker tools)
2026-01-03 21:35:44 +01:00
2026-01-08 21:49:26 +01:00
Deep dive: [Sandboxing ](/gateway/sandboxing )
2026-01-03 21:35:44 +01:00
### What it does
2026-01-09 12:44:23 +00:00
When `agents.defaults.sandbox` is enabled, **non-main sessions ** run tools inside a Docker
2026-01-03 21:35:44 +01:00
container. The gateway stays on your host, but the tool execution is isolated:
2026-01-31 18:31:49 +09:00
2026-01-07 02:31:51 +01:00
- scope: `"agent"` by default (one container + workspace per agent)
- scope: `"session"` for per-session isolation
- per-scope workspace folder mounted at `/workspace`
2026-01-09 12:44:23 +00:00
- optional agent workspace access (`agents.defaults.sandbox.workspaceAccess` )
2026-01-03 21:35:44 +01:00
- allow/deny tool policy (deny wins)
2026-01-07 09:32:49 +00:00
- inbound media is copied into the active sandbox workspace (`media/inbound/*` ) so tools can read it (with `workspaceAccess: "rw"` , this lands in the agent workspace)
2026-01-03 21:35:44 +01:00
2026-01-07 02:31:51 +01:00
Warning: `scope: "shared"` disables cross-session isolation. All sessions share
one container and one workspace.
2026-01-06 23:22:49 +01:00
2026-01-07 20:31:23 +01:00
### Per-agent sandbox profiles (multi-agent)
If you use multi-agent routing, each agent can override sandbox + tool settings:
2026-01-09 12:44:23 +00:00
`agents.list[].sandbox` and `agents.list[].tools` (plus `agents.list[].tools.sandbox.tools` ). This lets you run
2026-01-07 20:31:23 +01:00
mixed access levels in one gateway:
2026-01-31 18:31:49 +09:00
2026-01-07 20:31:23 +01:00
- Full access (personal agent)
- Read-only tools + read-only workspace (family/work agent)
- No filesystem/shell tools (public agent)
2026-02-07 15:40:35 -05:00
See [Multi-Agent Sandbox & Tools ](/tools/multi-agent-sandbox-tools ) for examples,
2026-01-07 20:31:23 +01:00
precedence, and troubleshooting.
2026-01-03 21:35:44 +01:00
### Default behavior
2026-01-30 03:15:10 +01:00
- Image: `openclaw-sandbox:bookworm-slim`
2026-01-07 02:31:51 +01:00
- One container per agent
2026-01-30 03:15:10 +01:00
- Agent workspace access: `workspaceAccess: "none"` (default) uses `~/.openclaw/sandboxes`
2026-01-12 03:42:49 +00:00
- `"ro"` keeps the sandbox workspace at `/workspace` and mounts the agent workspace read-only at `/agent` (disables `write` /`edit` /`apply_patch` )
2026-01-07 09:32:49 +00:00
- `"rw"` mounts the agent workspace read/write at `/workspace`
2026-01-03 21:35:44 +01:00
- Auto-prune: idle > 24h OR age > 7d
2026-01-04 14:32:47 +00:00
- Network: `none` by default (explicitly opt-in if you need egress)
2026-01-12 02:49:55 +00:00
- Default allow: `exec` , `process` , `read` , `write` , `edit` , `sessions_list` , `sessions_history` , `sessions_send` , `sessions_spawn` , `session_status`
2026-01-03 21:35:44 +01:00
- Default deny: `browser` , `canvas` , `nodes` , `cron` , `discord` , `gateway`
### Enable sandboxing
2026-01-19 01:35:17 +00:00
If you plan to install packages in `setupCommand` , note:
2026-01-31 18:31:49 +09:00
2026-01-19 01:35:17 +00:00
- Default `docker.network` is `"none"` (no egress).
- `readOnlyRoot: true` blocks package installs.
- `user` must be root for `apt-get` (omit `user` or set `user: "0:0"` ).
2026-01-31 18:31:49 +09:00
OpenClaw auto-recreates containers when `setupCommand` (or docker config) changes
unless the container was **recently used ** (within ~5 minutes). Hot containers
log a warning with the exact `openclaw sandbox recreate ...` command.
2026-01-19 01:35:17 +00:00
2026-01-03 21:35:44 +01:00
```json5
{
2026-01-09 12:44:23 +00:00
agents: {
defaults: {
sandbox: {
2026-01-31 21:13:13 +09:00
mode: "non-main", // off | non-main | all
scope: "agent", // session | agent | shared (agent is default)
workspaceAccess: "none", // none | ro | rw
workspaceRoot: "~/.openclaw/sandboxes",
2026-01-09 12:44:23 +00:00
docker: {
2026-01-31 21:13:13 +09:00
image: "openclaw-sandbox:bookworm-slim",
workdir: "/workspace",
2026-01-09 12:44:23 +00:00
readOnlyRoot: true,
2026-01-31 21:13:13 +09:00
tmpfs: ["/tmp", "/var/tmp", "/run"],
network: "none",
user: "1000:1000",
capDrop: ["ALL"],
env: { LANG: "C.UTF-8" },
setupCommand: "apt-get update && apt-get install -y git curl jq",
2026-01-09 12:44:23 +00:00
pidsLimit: 256,
2026-01-31 21:13:13 +09:00
memory: "1g",
memorySwap: "2g",
2026-01-09 12:44:23 +00:00
cpus: 1,
ulimits: {
nofile: { soft: 1024, hard: 2048 },
2026-01-31 18:31:49 +09:00
nproc: 256,
2026-01-09 12:44:23 +00:00
},
2026-01-31 21:13:13 +09:00
seccompProfile: "/path/to/seccomp.json",
apparmorProfile: "openclaw-sandbox",
dns: ["1.1.1.1", "8.8.8.8"],
extraHosts: ["internal.service:10.0.0.5"],
2026-01-04 14:32:47 +00:00
},
2026-01-09 12:44:23 +00:00
prune: {
idleHours: 24, // 0 disables idle pruning
2026-01-31 18:31:49 +09:00
maxAgeDays: 7, // 0 disables max-age pruning
},
},
},
2026-01-09 12:44:23 +00:00
},
tools: {
sandbox: {
2026-01-03 21:35:44 +01:00
tools: {
2026-01-31 18:31:49 +09:00
allow: [
2026-01-31 21:13:13 +09:00
"exec",
"process",
"read",
"write",
"edit",
"sessions_list",
"sessions_history",
"sessions_send",
"sessions_spawn",
"session_status",
2026-01-31 18:31:49 +09:00
],
2026-01-31 21:13:13 +09:00
deny: ["browser", "canvas", "nodes", "cron", "discord", "gateway"],
2026-01-31 18:31:49 +09:00
},
},
},
2026-01-03 21:35:44 +01:00
}
```
2026-01-09 12:44:23 +00:00
Hardening knobs live under `agents.defaults.sandbox.docker` :
2026-01-04 14:32:47 +00:00
`network` , `user` , `pidsLimit` , `memory` , `memorySwap` , `cpus` , `ulimits` ,
`seccompProfile` , `apparmorProfile` , `dns` , `extraHosts` .
2026-01-09 12:44:23 +00:00
Multi-agent: override `agents.defaults.sandbox.{docker,browser,prune}.*` per agent via `agents.list[].sandbox.{docker,browser,prune}.*`
(ignored when `agents.defaults.sandbox.scope` / `agents.list[].sandbox.scope` is `"shared"` ).
2026-01-08 00:52:15 +01:00
2026-01-03 21:35:44 +01:00
### Build the default sandbox image
```bash
scripts/sandbox-setup.sh
```
2026-01-30 03:15:10 +01:00
This builds `openclaw-sandbox:bookworm-slim` using `Dockerfile.sandbox` .
2026-01-03 21:35:44 +01:00
2026-01-04 16:02:28 +00:00
### Sandbox common image (optional)
2026-01-31 18:31:49 +09:00
2026-01-04 16:02:28 +00:00
If you want a sandbox image with common build tooling (Node, Go, Rust, etc.), build the common image:
```bash
scripts/sandbox-common-setup.sh
```
2026-01-30 03:15:10 +01:00
This builds `openclaw-sandbox-common:bookworm-slim` . To use it:
2026-01-04 16:02:28 +00:00
```json5
{
2026-01-31 18:31:49 +09:00
agents: {
defaults: {
2026-01-31 21:13:13 +09:00
sandbox: { docker: { image: "openclaw-sandbox-common:bookworm-slim" } },
2026-01-31 18:31:49 +09:00
},
},
2026-01-04 16:02:28 +00:00
}
```
2026-01-03 22:11:43 +01:00
### Sandbox browser image
To run the browser tool inside the sandbox, build the browser image:
```bash
scripts/sandbox-browser-setup.sh
```
2026-01-30 03:15:10 +01:00
This builds `openclaw-sandbox-browser:bookworm-slim` using
2026-01-03 22:11:43 +01:00
`Dockerfile.sandbox-browser` . The container runs Chromium with CDP enabled and
an optional noVNC observer (headful via Xvfb).
Notes:
2026-01-31 18:31:49 +09:00
2026-01-03 22:11:43 +01:00
- Headful (Xvfb) reduces bot blocking vs headless.
2026-01-09 12:44:23 +00:00
- Headless can still be used by setting `agents.defaults.sandbox.browser.headless=true` .
2026-01-03 22:11:43 +01:00
- No full desktop environment (GNOME) is needed; Xvfb provides the display.
Use config:
```json5
{
2026-01-09 12:44:23 +00:00
agents: {
defaults: {
sandbox: {
2026-01-31 18:31:49 +09:00
browser: { enabled: true },
},
},
},
2026-01-03 22:11:43 +01:00
}
```
Custom browser image:
```json5
{
2026-01-09 12:44:23 +00:00
agents: {
defaults: {
2026-01-31 21:13:13 +09:00
sandbox: { browser: { image: "my-openclaw-browser" } },
2026-01-31 18:31:49 +09:00
},
},
2026-01-03 22:11:43 +01:00
}
```
When enabled, the agent receives:
2026-01-31 18:31:49 +09:00
2026-01-03 22:11:43 +01:00
- a sandbox browser control URL (for the `browser` tool)
- a noVNC URL (if enabled and headless=false)
Remember: if you use an allowlist for tools, add `browser` (and remove it from
deny) or the tool remains blocked.
2026-01-09 12:44:23 +00:00
Prune rules (`agents.defaults.sandbox.prune` ) apply to browser containers too.
2026-01-03 22:11:43 +01:00
2026-01-03 21:35:44 +01:00
### Custom sandbox image
Build your own image and point config to it:
```bash
2026-01-30 03:15:10 +01:00
docker build -t my-openclaw-sbx -f Dockerfile.sandbox .
2026-01-03 21:35:44 +01:00
```
```json5
{
2026-01-09 12:44:23 +00:00
agents: {
defaults: {
2026-01-31 21:13:13 +09:00
sandbox: { docker: { image: "my-openclaw-sbx" } },
2026-01-31 18:31:49 +09:00
},
},
2026-01-03 21:35:44 +01:00
}
```
### Tool policy (allow/deny)
- `deny` wins over `allow` .
- If `allow` is empty: all tools (except deny) are available.
- If `allow` is non-empty: only tools in `allow` are available (minus deny).
### Pruning strategy
Two knobs:
2026-01-31 18:31:49 +09:00
2026-01-03 21:35:44 +01:00
- `prune.idleHours` : remove containers not used in X hours (0 = disable)
- `prune.maxAgeDays` : remove containers older than X days (0 = disable)
Example:
2026-01-31 18:31:49 +09:00
2026-01-03 21:35:44 +01:00
- Keep busy sessions but cap lifetime:
`idleHours: 24` , `maxAgeDays: 7`
- Never prune:
`idleHours: 0` , `maxAgeDays: 0`
### Security notes
2026-01-31 18:31:49 +09:00
- Hard wall only applies to **tools ** (exec/read/write/edit/apply_patch).
- Host-only tools like browser/camera/canvas are blocked by default.
2026-01-03 21:35:44 +01:00
- Allowing `browser` in sandbox **breaks isolation ** (browser runs on host).
## Troubleshooting
2026-01-30 03:15:10 +01:00
- Image missing: build with [`scripts/sandbox-setup.sh` ](https://github.com/openclaw/openclaw/blob/main/scripts/sandbox-setup.sh ) or set `agents.defaults.sandbox.docker.image` .
2026-01-03 21:35:44 +01:00
- Container not running: it will auto-create per session on demand.
- Permission errors in sandbox: set `docker.user` to a UID:GID that matches your
mounted workspace ownership (or chown the workspace folder).
2026-01-30 03:15:10 +01:00
- Custom tools not found: OpenClaw runs commands with `sh -lc` (login shell), which
2026-01-15 02:58:20 +00:00
sources `/etc/profile` and may reset PATH. Set `docker.env.PATH` to prepend your
custom tool paths (e.g., `/custom/bin:/usr/local/share/npm-global/bin` ), or add
a script under `/etc/profile.d/` in your Dockerfile.