From 9117836981ffa8fac74188e6cca7e2602f2d8bb6 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Thu, 19 Mar 2026 12:07:37 -0700 Subject: [PATCH] =?UTF-8?q?docs:=20deep=20rewrite=20Docker=20page=20(851?= =?UTF-8?q?=E2=86=92375=20lines),=20trim=20sandbox=20duplication,=20add=20?= =?UTF-8?q?Steps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/install/docker.md | 993 +++++++++++------------------------------ 1 file changed, 259 insertions(+), 734 deletions(-) diff --git a/docs/install/docker.md b/docs/install/docker.md index b072ede04da..aaaa11f9917 100644 --- a/docs/install/docker.md +++ b/docs/install/docker.md @@ -13,221 +13,84 @@ Docker is **optional**. Use it only if you want a containerized gateway or to va ## Is Docker right for me? - **Yes**: you want an isolated, throwaway gateway environment or to run OpenClaw on a host without local installs. -- **No**: you’re running on your own machine and just want the fastest dev loop. Use the normal install flow instead. +- **No**: you are 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). -This guide covers: - -- Containerized Gateway (full OpenClaw in Docker) -- Per-session Agent Sandbox (host gateway + Docker-isolated agent tools) - -Sandboxing details: [Sandboxing](/gateway/sandboxing) - -## On this page - -- [Containerized Gateway](#containerized-gateway-docker-compose) — run the full OpenClaw Gateway in Docker -- [Agent Sandbox](#agent-sandbox-host-gateway--docker-tools) — isolate agent tool execution in containers while the gateway runs on the host -- [Troubleshooting](#troubleshooting) - -## Requirements +## Prerequisites - Docker Desktop (or Docker Engine) + Docker Compose v2 - At least 2 GB RAM for image build (`pnpm install` may be OOM-killed on 1 GB hosts with exit 137) -- Enough disk for images + logs +- Enough disk for images and logs - If running on a VPS/public host, review [Security hardening for network exposure](/gateway/security#0-4-network-exposure-bind-port-firewall), especially Docker `DOCKER-USER` firewall policy. -## Containerized Gateway (Docker Compose) +## Containerized Gateway -### Quick start (recommended) + + + From the repo root, run the setup script: - -Docker defaults here assume bind modes (`lan`/`loopback`), not host aliases. Use bind -mode values in `gateway.bind` (for example `lan` or `loopback`), not host aliases like -`0.0.0.0` or `localhost`. - + ```bash + ./docker-setup.sh + ``` -From repo root: + This builds the gateway image locally. To use a pre-built image instead: -```bash -./docker-setup.sh -``` + ```bash + export OPENCLAW_IMAGE="ghcr.io/openclaw/openclaw:latest" + ./docker-setup.sh + ``` -This script: + Pre-built images are published at the + [GitHub Container Registry](https://github.com/openclaw/openclaw/pkgs/container/openclaw). + Common tags: `main`, `latest`, `` (e.g. `2026.2.26`). -- builds the gateway image locally (or pulls a remote image if `OPENCLAW_IMAGE` is set) -- runs onboarding -- prints optional provider setup hints -- starts the gateway via Docker Compose -- generates a gateway token and writes it to `.env` + -Optional env vars: + + The setup script runs onboarding automatically. It will: -- `OPENCLAW_IMAGE` — use a remote image instead of building locally (e.g. `ghcr.io/openclaw/openclaw:latest`) -- `OPENCLAW_DOCKER_APT_PACKAGES` — install extra apt packages during build -- `OPENCLAW_EXTENSIONS` — pre-install extension dependencies at build time (space-separated extension names, e.g. `diagnostics-otel matrix`) -- `OPENCLAW_EXTRA_MOUNTS` — add extra host bind mounts -- `OPENCLAW_HOME_VOLUME` — persist `/home/node` in a named volume -- `OPENCLAW_SANDBOX` — opt in to Docker gateway sandbox bootstrap. Only explicit truthy values enable it: `1`, `true`, `yes`, `on` -- `OPENCLAW_INSTALL_DOCKER_CLI` — build arg passthrough for local image builds (`1` installs Docker CLI in the image). `docker-setup.sh` sets this automatically when `OPENCLAW_SANDBOX=1` for local builds. -- `OPENCLAW_DOCKER_SOCKET` — override Docker socket path (default: `DOCKER_HOST=unix://...` path, else `/var/run/docker.sock`) -- `OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1` — break-glass: allow trusted private-network - `ws://` targets for CLI/onboarding client paths (default is loopback-only) -- `OPENCLAW_BROWSER_DISABLE_GRAPHICS_FLAGS=0` — disable container browser hardening flags - `--disable-3d-apis`, `--disable-software-rasterizer`, `--disable-gpu` when you need - WebGL/3D compatibility. -- `OPENCLAW_BROWSER_DISABLE_EXTENSIONS=0` — keep extensions enabled when browser - flows require them (default keeps extensions disabled in sandbox browser). -- `OPENCLAW_BROWSER_RENDERER_PROCESS_LIMIT=` — set Chromium renderer process - limit; set to `0` to skip the flag and use Chromium default behavior. + - prompt for provider API keys + - generate a gateway token and write it to `.env` + - start the gateway via Docker Compose -After it finishes: + -- Open `http://127.0.0.1:18789/` in your browser. -- Paste the token into the Control UI (Settings → token). -- Need the URL again? Run `docker compose run --rm openclaw-cli dashboard --no-open`. + + Open `http://127.0.0.1:18789/` in your browser and paste the token into + Settings. -### Enable agent sandbox for Docker gateway (opt-in) + Need the URL again? -`docker-setup.sh` can also bootstrap `agents.defaults.sandbox.*` for Docker -deployments. + ```bash + docker compose run --rm openclaw-cli dashboard --no-open + ``` -Enable with: + -```bash -export OPENCLAW_SANDBOX=1 -./docker-setup.sh -``` + + Use the CLI container to add messaging channels: -Custom socket path (for example rootless Docker): + ```bash + # WhatsApp (QR) + docker compose run --rm openclaw-cli channels login -```bash -export OPENCLAW_SANDBOX=1 -export OPENCLAW_DOCKER_SOCKET=/run/user/1000/docker.sock -./docker-setup.sh -``` + # Telegram + docker compose run --rm openclaw-cli channels add --channel telegram --token "" -Notes: + # Discord + docker compose run --rm openclaw-cli channels add --channel discord --token "" + ``` -- The script mounts `docker.sock` only after sandbox prerequisites pass. -- If sandbox setup cannot be completed, the script resets - `agents.defaults.sandbox.mode` to `off` to avoid stale/broken sandbox config - on reruns. -- If `Dockerfile.sandbox` is missing, the script prints a warning and continues; - build `openclaw-sandbox:bookworm-slim` with `scripts/sandbox-setup.sh` if - needed. -- For non-local `OPENCLAW_IMAGE` values, the image must already contain Docker - CLI support for sandbox execution. + Docs: [WhatsApp](/channels/whatsapp), [Telegram](/channels/telegram), [Discord](/channels/discord) -### Automation/CI (non-interactive, no TTY noise) + + -For scripts and CI, disable Compose pseudo-TTY allocation with `-T`: +### Manual flow -```bash -docker compose run -T --rm openclaw-cli gateway probe -docker compose run -T --rm openclaw-cli devices list --json -``` - -If your automation exports no Claude session vars, leaving them unset now resolves to -empty values by default in `docker-compose.yml` to avoid repeated "variable is not set" -warnings. - -### Shared-network security note (CLI + gateway) - -`openclaw-cli` uses `network_mode: "service:openclaw-gateway"` so CLI commands can -reliably reach the gateway over `127.0.0.1` in Docker. - -Treat this as a shared trust boundary: loopback binding is not isolation between these two -containers. If you need stronger separation, run commands from a separate container/host -network path instead of the bundled `openclaw-cli` service. - -To reduce impact if the CLI process is compromised, the compose config drops -`NET_RAW`/`NET_ADMIN` and enables `no-new-privileges` on `openclaw-cli`. - -It writes config/workspace on the host: - -- `~/.openclaw/` -- `~/.openclaw/workspace` - -Running on a VPS? See [Hetzner (Docker VPS)](/install/hetzner). - -### Use a remote image (skip local build) - -Official pre-built images are published at: - -- [GitHub Container Registry package](https://github.com/openclaw/openclaw/pkgs/container/openclaw) - -Use image name `ghcr.io/openclaw/openclaw` (not similarly named Docker Hub -images). - -Common tags: - -- `main` — latest build from `main` -- `` — release tag builds (for example `2026.2.26`) -- `latest` — latest stable release tag - -### Base image metadata - -The main Docker image currently uses: - -- `node:24-bookworm` - -The docker image now publishes OCI base-image annotations (sha256 is an example, -and points at the pinned multi-arch manifest list for that tag): - -- `org.opencontainers.image.base.name=docker.io/library/node:24-bookworm` -- `org.opencontainers.image.base.digest=sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b` -- `org.opencontainers.image.source=https://github.com/openclaw/openclaw` -- `org.opencontainers.image.url=https://openclaw.ai` -- `org.opencontainers.image.documentation=https://docs.openclaw.ai/install/docker` -- `org.opencontainers.image.licenses=MIT` -- `org.opencontainers.image.title=OpenClaw` -- `org.opencontainers.image.description=OpenClaw gateway and CLI runtime container image` -- `org.opencontainers.image.revision=` -- `org.opencontainers.image.version=` -- `org.opencontainers.image.created=` - -Reference: [OCI image annotations](https://github.com/opencontainers/image-spec/blob/main/annotations.md) - -Release context: this repository's tagged history already uses Bookworm in -`v2026.2.22` and earlier 2026 tags (for example `v2026.2.21`, `v2026.2.9`). - -By default the setup script builds the image from source. To pull a pre-built -image instead, set `OPENCLAW_IMAGE` before running the script: - -```bash -export OPENCLAW_IMAGE="ghcr.io/openclaw/openclaw:latest" -./docker-setup.sh -``` - -The script detects that `OPENCLAW_IMAGE` is not the default `openclaw:local` and -runs `docker pull` instead of `docker build`. Everything else (onboarding, -gateway start, token generation) works the same way. - -`docker-setup.sh` still runs from the repository root because it uses the local -`docker-compose.yml` and helper files. `OPENCLAW_IMAGE` skips local image build -time; it does not replace the compose/setup workflow. - -### 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. - -### Manual flow (compose) +If you prefer to run each step yourself instead of using the setup script: ```bash docker build -t openclaw:local -f Dockerfile . @@ -235,378 +98,212 @@ docker compose run --rm openclaw-cli onboard docker compose up -d openclaw-gateway ``` -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 -``` - -### 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 -``` - -More detail: [Dashboard](/web/dashboard), [Devices](/cli/devices). - -### Extra mounts (optional) - -If you want to mount additional host directories into the containers, set -`OPENCLAW_EXTRA_MOUNTS` before running `docker-setup.sh`. This accepts a -comma-separated list of Docker bind mounts and applies them to both -`openclaw-gateway` and `openclaw-cli` by generating `docker-compose.extra.yml`. - -Example: - -```bash -export OPENCLAW_EXTRA_MOUNTS="$HOME/.codex:/home/node/.codex:ro,$HOME/github:/home/node/github:rw" -./docker-setup.sh -``` - -Notes: - -- Paths must be shared with Docker Desktop on macOS/Windows. -- Each entry must be `source:target[:options]` with no spaces, tabs, or newlines. -- If you edit `OPENCLAW_EXTRA_MOUNTS`, rerun `docker-setup.sh` to regenerate the - extra compose file. -- `docker-compose.extra.yml` is generated. Don’t hand-edit it. - -### Persist the entire container home (optional) - -If you want `/home/node` to persist across container recreation, set a named -volume via `OPENCLAW_HOME_VOLUME`. This creates a Docker volume and mounts it at -`/home/node`, while keeping the standard config/workspace bind mounts. Use a -named volume here (not a bind path); for bind mounts, use -`OPENCLAW_EXTRA_MOUNTS`. - -Example: - -```bash -export OPENCLAW_HOME_VOLUME="openclaw_home" -./docker-setup.sh -``` - -You can combine this with extra mounts: - -```bash -export OPENCLAW_HOME_VOLUME="openclaw_home" -export OPENCLAW_EXTRA_MOUNTS="$HOME/.codex:/home/node/.codex:ro,$HOME/github:/home/node/github:rw" -./docker-setup.sh -``` - -Notes: - -- Named volumes must match `^[A-Za-z0-9][A-Za-z0-9_.-]*$`. -- If you change `OPENCLAW_HOME_VOLUME`, rerun `docker-setup.sh` to regenerate the - extra compose file. -- The named volume persists until removed with `docker volume rm `. - -### Install extra apt packages (optional) - -If you need system packages inside the image (for example, build tools or media -libraries), set `OPENCLAW_DOCKER_APT_PACKAGES` before running `docker-setup.sh`. -This installs the packages during the image build, so they persist even if the -container is deleted. - -Example: - -```bash -export OPENCLAW_DOCKER_APT_PACKAGES="ffmpeg build-essential" -./docker-setup.sh -``` - -Notes: - -- This accepts a space-separated list of apt package names. -- If you change `OPENCLAW_DOCKER_APT_PACKAGES`, rerun `docker-setup.sh` to rebuild - the image. - -### Pre-install extension dependencies (optional) - -Extensions with their own `package.json` (e.g. `diagnostics-otel`, `matrix`, -`msteams`) install their npm dependencies on first load. To bake those -dependencies into the image instead, set `OPENCLAW_EXTENSIONS` before -running `docker-setup.sh`: - -```bash -export OPENCLAW_EXTENSIONS="diagnostics-otel matrix" -./docker-setup.sh -``` - -Or when building directly: - -```bash -docker build --build-arg OPENCLAW_EXTENSIONS="diagnostics-otel matrix" . -``` - -Notes: - -- This accepts a space-separated list of extension directory names (under `extensions/`). -- Only extensions with a `package.json` are affected; lightweight plugins without one are ignored. -- If you change `OPENCLAW_EXTENSIONS`, rerun `docker-setup.sh` to rebuild - the image. - -### 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: - -1. **Persist `/home/node`** so browser downloads and tool caches survive: - -```bash -export OPENCLAW_HOME_VOLUME="openclaw_home" -./docker-setup.sh -``` - -2. **Bake system deps into the image** (repeatable + persistent): - -```bash -export OPENCLAW_DOCKER_APT_PACKAGES="git curl jq" -./docker-setup.sh -``` - -3. **Install Playwright browsers without `npx`** (avoids npm override conflicts): - -```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. - -4. **Persist Playwright browser downloads**: - -- 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. - -### 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:24-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 - -CMD ["node","dist/index.js"] -``` - -### Channel setup (optional) - -Use the CLI container to configure channels, then restart the gateway if needed. - -WhatsApp (QR): - -```bash -docker compose run --rm openclaw-cli channels login -``` - -Telegram (bot token): - -```bash -docker compose run --rm openclaw-cli channels add --channel telegram --token "" -``` - -Discord (bot token): - -```bash -docker compose run --rm openclaw-cli channels add --channel discord --token "" -``` - -Docs: [WhatsApp](/channels/whatsapp), [Telegram](/channels/telegram), [Discord](/channels/discord) - -### 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. + +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 with `-f docker-compose.yml -f docker-compose.extra.yml`. + + +### Environment variables + +The setup script accepts these optional environment variables: + +| Variable | Purpose | +| ------------------------------ | ---------------------------------------------------------------- | +| `OPENCLAW_IMAGE` | Use a remote image instead of building locally | +| `OPENCLAW_DOCKER_APT_PACKAGES` | Install extra apt packages during build (space-separated) | +| `OPENCLAW_EXTENSIONS` | Pre-install extension deps at build time (space-separated names) | +| `OPENCLAW_EXTRA_MOUNTS` | Extra host bind mounts (comma-separated `source:target[:opts]`) | +| `OPENCLAW_HOME_VOLUME` | Persist `/home/node` in a named Docker volume | +| `OPENCLAW_SANDBOX` | Opt in to sandbox bootstrap (`1`, `true`, `yes`, `on`) | +| `OPENCLAW_DOCKER_SOCKET` | Override Docker socket path | ### Health checks Container probe endpoints (no auth required): ```bash -curl -fsS http://127.0.0.1:18789/healthz -curl -fsS http://127.0.0.1:18789/readyz +curl -fsS http://127.0.0.1:18789/healthz # liveness +curl -fsS http://127.0.0.1:18789/readyz # readiness ``` -Aliases: `/health` and `/ready`. +The Docker image includes a built-in `HEALTHCHECK` that pings `/healthz`. +If checks keep failing, Docker marks the container as `unhealthy` and +orchestration systems can restart or replace it. -`/healthz` is a shallow liveness probe for "the gateway process is up". -`/readyz` stays ready during startup grace, then becomes `503` only if required -managed channels are still disconnected after grace or disconnect later. - -The Docker image includes a built-in `HEALTHCHECK` that pings `/healthz` in the -background. In plain terms: Docker keeps checking if OpenClaw is still -responsive. If checks keep failing, Docker marks the container as `unhealthy`, -and orchestration systems (Docker Compose restart policy, Swarm, Kubernetes, -etc.) can automatically restart or replace it. - -Authenticated deep health snapshot (gateway + channels): +Authenticated deep health snapshot: ```bash docker compose exec openclaw-gateway node dist/index.js health --token "$OPENCLAW_GATEWAY_TOKEN" ``` -### E2E smoke test (Docker) - -```bash -scripts/e2e/onboard-docker.sh -``` - -### QR import smoke test (Docker) - -```bash -pnpm test:docker:qr -``` - -### LAN vs loopback (Docker Compose) +### LAN vs loopback `docker-setup.sh` defaults `OPENCLAW_GATEWAY_BIND=lan` so host access to `http://127.0.0.1:18789` works with Docker port publishing. -- `lan` (default): host browser + host CLI can reach the published gateway port. +- `lan` (default): host browser and host CLI can reach the published gateway port. - `loopback`: only processes inside the container network namespace can reach - the gateway directly; host-published port access may fail. + the gateway directly. -The setup script also pins `gateway.mode=local` after onboarding so Docker CLI -commands default to local loopback targeting. + +Use bind mode values in `gateway.bind` (`lan` / `loopback` / `custom` / +`tailnet` / `auto`), not host aliases like `0.0.0.0` or `127.0.0.1`. + -Legacy config note: use bind mode values in `gateway.bind` (`lan` / `loopback` / -`custom` / `tailnet` / `auto`), not host aliases (`0.0.0.0`, `127.0.0.1`, -`localhost`, `::`, `::1`). +### Storage and persistence -If you see `Gateway target: ws://172.x.x.x:18789` or repeated `pairing required` -errors from Docker CLI commands, run: +Docker Compose bind-mounts `OPENCLAW_CONFIG_DIR` to `/home/node/.openclaw` and +`OPENCLAW_WORKSPACE_DIR` to `/home/node/.openclaw/workspace`, so those paths +survive container replacement. + +For full persistence details on VM deployments, see +[Docker VM Runtime - What persists where](/install/docker-vm-runtime#what-persists-where). + +**Disk growth hotspots:** watch `media/`, session JSONL files, `cron/runs/*.jsonl`, +and rolling file logs under `/tmp/openclaw/`. + +### Shell helpers (optional) + +For easier day-to-day Docker management, install `ClawDock`: ```bash -docker compose run --rm openclaw-cli config set gateway.mode local -docker compose run --rm openclaw-cli config set gateway.bind lan -docker compose run --rm openclaw-cli devices list --url ws://127.0.0.1:18789 +mkdir -p ~/.clawdock && curl -sL https://raw.githubusercontent.com/openclaw/openclaw/main/scripts/shell-helpers/clawdock-helpers.sh -o ~/.clawdock/clawdock-helpers.sh +echo 'source ~/.clawdock/clawdock-helpers.sh' >> ~/.zshrc && source ~/.zshrc ``` -### Notes +Then use `clawdock-start`, `clawdock-stop`, `clawdock-dashboard`, etc. Run +`clawdock-help` for all commands. +See the [`ClawDock` Helper README](https://github.com/openclaw/openclaw/blob/main/scripts/shell-helpers/README.md). -- Gateway bind defaults to `lan` for container use (`OPENCLAW_GATEWAY_BIND`). -- Dockerfile CMD uses `--allow-unconfigured`; mounted config with `gateway.mode` not `local` will still start. Override CMD to enforce the guard. -- The gateway container is the source of truth for sessions (`~/.openclaw/agents//sessions/`). + + + ```bash + export OPENCLAW_SANDBOX=1 + ./docker-setup.sh + ``` -### Storage model + Custom socket path (e.g. rootless Docker): -- **Persistent host data:** Docker Compose bind-mounts `OPENCLAW_CONFIG_DIR` to `/home/node/.openclaw` and `OPENCLAW_WORKSPACE_DIR` to `/home/node/.openclaw/workspace`, so those paths survive container replacement. -- **Ephemeral sandbox tmpfs:** when `agents.defaults.sandbox` is enabled, the sandbox containers use `tmpfs` for `/tmp`, `/var/tmp`, and `/run`. Those mounts are separate from the top-level Compose stack and disappear with the sandbox container. -- **Disk growth hotspots:** watch `media/`, `agents//sessions/sessions.json`, transcript JSONL files, `cron/runs/*.jsonl`, and rolling file logs under `/tmp/openclaw/` (or your configured `logging.file`). If you also run the macOS app outside Docker, its service logs are separate again: `~/.openclaw/logs/gateway.log`, `~/.openclaw/logs/gateway.err.log`, and `/tmp/openclaw/openclaw-gateway.log`. + ```bash + export OPENCLAW_SANDBOX=1 + export OPENCLAW_DOCKER_SOCKET=/run/user/1000/docker.sock + ./docker-setup.sh + ``` -## Agent Sandbox (host gateway + Docker tools) + The script mounts `docker.sock` only after sandbox prerequisites pass. If + sandbox setup cannot complete, the script resets `agents.defaults.sandbox.mode` + to `off`. -Deep dive: [Sandboxing](/gateway/sandboxing) + -### What it does + + Disable Compose pseudo-TTY allocation with `-T`: -When `agents.defaults.sandbox` is enabled, **non-main sessions** run tools inside a Docker -container. The gateway stays on your host, but the tool execution is isolated: + ```bash + docker compose run -T --rm openclaw-cli gateway probe + docker compose run -T --rm openclaw-cli devices list --json + ``` -- scope: `"agent"` by default (one container + workspace per agent) -- scope: `"session"` for per-session isolation -- per-scope workspace folder mounted at `/workspace` -- optional agent workspace access (`agents.defaults.sandbox.workspaceAccess`) -- allow/deny tool policy (deny wins) -- 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) + -Warning: `scope: "shared"` disables cross-session isolation. All sessions share -one container and one workspace. + + `openclaw-cli` uses `network_mode: "service:openclaw-gateway"` so CLI + commands can reach the gateway over `127.0.0.1`. Treat this as a shared + trust boundary. The compose config drops `NET_RAW`/`NET_ADMIN` and enables + `no-new-privileges` on `openclaw-cli`. + -### Per-agent sandbox profiles (multi-agent) + + 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: -If you use multi-agent routing, each agent can override sandbox + tool settings: -`agents.list[].sandbox` and `agents.list[].tools` (plus `agents.list[].tools.sandbox.tools`). This lets you run -mixed access levels in one gateway: + ```bash + sudo chown -R 1000:1000 /path/to/openclaw-config /path/to/openclaw-workspace + ``` -- Full access (personal agent) -- Read-only tools + read-only workspace (family/work agent) -- No filesystem/shell tools (public agent) + -See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for examples, -precedence, and troubleshooting. + + Order your Dockerfile so dependency layers are cached. This avoids re-running + `pnpm install` unless lockfiles change: -### Default behavior + ```dockerfile + FROM node:24-bookworm + RUN curl -fsSL https://bun.sh/install | bash + ENV PATH="/root/.bun/bin:${PATH}" + RUN corepack enable + WORKDIR /app + 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 + CMD ["node","dist/index.js"] + ``` -- Image: `openclaw-sandbox:bookworm-slim` -- One container per agent -- Agent workspace access: `workspaceAccess: "none"` (default) uses `~/.openclaw/sandboxes` - - `"ro"` keeps the sandbox workspace at `/workspace` and mounts the agent workspace read-only at `/agent` (disables `write`/`edit`/`apply_patch`) - - `"rw"` mounts the agent workspace read/write at `/workspace` -- Auto-prune: idle > 24h OR age > 7d -- Network: `none` by default (explicitly opt-in if you need egress) - - `host` is blocked. - - `container:` is blocked by default (namespace-join risk). -- Default allow: `exec`, `process`, `read`, `write`, `edit`, `sessions_list`, `sessions_history`, `sessions_send`, `sessions_spawn`, `session_status` -- Default deny: `browser`, `canvas`, `nodes`, `cron`, `discord`, `gateway` + -### Enable sandboxing + + The default image is security-first and runs as non-root `node`. For a more + full-featured container: -If you plan to install packages in `setupCommand`, note: + 1. **Persist `/home/node`**: `export OPENCLAW_HOME_VOLUME="openclaw_home"` + 2. **Bake system deps**: `export OPENCLAW_DOCKER_APT_PACKAGES="git curl jq"` + 3. **Install Playwright browsers**: + ```bash + docker compose run --rm openclaw-cli \ + node /app/node_modules/playwright-core/cli.js install chromium + ``` + 4. **Persist browser downloads**: set + `PLAYWRIGHT_BROWSERS_PATH=/home/node/.cache/ms-playwright` and use + `OPENCLAW_HOME_VOLUME` or `OPENCLAW_EXTRA_MOUNTS`. -- Default `docker.network` is `"none"` (no egress). -- `docker.network: "host"` is blocked. -- `docker.network: "container:"` is blocked by default. -- Break-glass override: `agents.defaults.sandbox.docker.dangerouslyAllowContainerNamespaceJoin: true`. -- `readOnlyRoot: true` blocks package installs. -- `user` must be root for `apt-get` (omit `user` or set `user: "0:0"`). - 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. + + + + If you pick OpenAI Codex OAuth in the wizard, it opens a browser URL. In + Docker or headless setups, copy the full redirect URL you land on and paste + it back into the wizard to finish auth. + + + + The main Docker image uses `node:24-bookworm` and publishes OCI base-image + annotations including `org.opencontainers.image.base.name`, + `org.opencontainers.image.source`, and others. See + [OCI image annotations](https://github.com/opencontainers/image-spec/blob/main/annotations.md). + + + +### Running on a VPS? + +See [Hetzner (Docker VPS)](/install/hetzner) and +[Docker VM Runtime](/install/docker-vm-runtime) for shared VM deployment steps +including binary baking, persistence, and updates. + +## Agent Sandbox + +When `agents.defaults.sandbox` is enabled, the gateway runs agent tool execution +(shell, file read/write, etc.) inside isolated Docker containers while the +gateway itself stays on the host. This gives you a hard wall around untrusted or +multi-tenant agent sessions without containerizing the entire gateway. + +Sandbox scope can be per-agent (default), per-session, or shared. Each scope +gets its own workspace mounted at `/workspace`. You can also configure +allow/deny tool policies, network isolation, resource limits, and browser +containers. + +For full configuration, images, security notes, and multi-agent profiles, see: + +- [Sandboxing](/gateway/sandboxing) -- complete sandbox reference +- [OpenShell](/gateway/openshell) -- interactive shell access to sandbox containers +- [Multi-Agent Sandbox and Tools](/tools/multi-agent-sandbox-tools) -- per-agent overrides + +### Quick enable ```json5 { @@ -614,237 +311,65 @@ If you plan to install packages in `setupCommand`, note: defaults: { sandbox: { mode: "non-main", // off | non-main | all - scope: "agent", // session | agent | shared (agent is default) - workspaceAccess: "none", // none | ro | rw - workspaceRoot: "~/.openclaw/sandboxes", - docker: { - image: "openclaw-sandbox:bookworm-slim", - workdir: "/workspace", - readOnlyRoot: true, - 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", - pidsLimit: 256, - memory: "1g", - memorySwap: "2g", - cpus: 1, - ulimits: { - nofile: { soft: 1024, hard: 2048 }, - nproc: 256, - }, - seccompProfile: "/path/to/seccomp.json", - apparmorProfile: "openclaw-sandbox", - dns: ["1.1.1.1", "8.8.8.8"], - extraHosts: ["internal.service:10.0.0.5"], - }, - prune: { - idleHours: 24, // 0 disables idle pruning - maxAgeDays: 7, // 0 disables max-age pruning - }, - }, - }, - }, - tools: { - sandbox: { - tools: { - allow: [ - "exec", - "process", - "read", - "write", - "edit", - "sessions_list", - "sessions_history", - "sessions_send", - "sessions_spawn", - "session_status", - ], - deny: ["browser", "canvas", "nodes", "cron", "discord", "gateway"], + scope: "agent", // session | agent | shared }, }, }, } ``` -Hardening knobs live under `agents.defaults.sandbox.docker`: -`network`, `user`, `pidsLimit`, `memory`, `memorySwap`, `cpus`, `ulimits`, -`seccompProfile`, `apparmorProfile`, `dns`, `extraHosts`, -`dangerouslyAllowContainerNamespaceJoin` (break-glass only). - -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"`). - -### Build the default sandbox image +Build the default sandbox image: ```bash scripts/sandbox-setup.sh ``` -This builds `openclaw-sandbox:bookworm-slim` using `Dockerfile.sandbox`. - -### Sandbox common image (optional) - -If you want a sandbox image with common build tooling (Node, Go, Rust, etc.), build the common image: - -```bash -scripts/sandbox-common-setup.sh -``` - -This builds `openclaw-sandbox-common:bookworm-slim`. To use it: - -```json5 -{ - agents: { - defaults: { - sandbox: { docker: { image: "openclaw-sandbox-common:bookworm-slim" } }, - }, - }, -} -``` - -### Sandbox browser image - -To run the browser tool inside the sandbox, build the browser image: - -```bash -scripts/sandbox-browser-setup.sh -``` - -This builds `openclaw-sandbox-browser:bookworm-slim` using -`Dockerfile.sandbox-browser`. The container runs Chromium with CDP enabled and -an optional noVNC observer (headful via Xvfb). - -Notes: - -- Docker and other headless/container browser flows stay on raw CDP. Chrome MCP `existing-session` is for host-local Chrome, not container takeover. -- Headful (Xvfb) reduces bot blocking vs headless. -- Headless can still be used by setting `agents.defaults.sandbox.browser.headless=true`. -- No full desktop environment (GNOME) is needed; Xvfb provides the display. -- Browser containers default to a dedicated Docker network (`openclaw-sandbox-browser`) instead of global `bridge`. -- Optional `agents.defaults.sandbox.browser.cdpSourceRange` restricts container-edge CDP ingress by CIDR (for example `172.21.0.1/32`). -- noVNC observer access is password-protected by default; OpenClaw provides a short-lived observer token URL that serves a local bootstrap page and keeps the password in URL fragment (instead of URL query). -- Browser container startup defaults are conservative for shared/container workloads, including: - - `--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-software-rasterizer` - - `--disable-gpu` - - `--disable-dev-shm-usage` - - `--disable-background-networking` - - `--disable-features=TranslateUI` - - `--disable-breakpad` - - `--disable-crash-reporter` - - `--metrics-recording-only` - - `--renderer-process-limit=2` - - `--no-zygote` - - `--disable-extensions` - - If `agents.defaults.sandbox.browser.noSandbox` is set, `--no-sandbox` and - `--disable-setuid-sandbox` are also appended. - - The three graphics hardening flags above are optional. If your workload needs - WebGL/3D, set `OPENCLAW_BROWSER_DISABLE_GRAPHICS_FLAGS=0` to run without - `--disable-3d-apis`, `--disable-software-rasterizer`, and `--disable-gpu`. - - Extension behavior is controlled by `--disable-extensions` and can be disabled - (enables extensions) via `OPENCLAW_BROWSER_DISABLE_EXTENSIONS=0` for - extension-dependent pages or extensions-heavy workflows. - - `--renderer-process-limit=2` is also configurable with - `OPENCLAW_BROWSER_RENDERER_PROCESS_LIMIT`; set `0` to let Chromium choose its - default process limit when browser concurrency needs tuning. - -Defaults are applied by default in the bundled image. If you need different -Chromium flags, use a custom browser image and provide your own entrypoint. - -Use config: - -```json5 -{ - agents: { - defaults: { - sandbox: { - browser: { enabled: true }, - }, - }, - }, -} -``` - -Custom browser image: - -```json5 -{ - agents: { - defaults: { - sandbox: { browser: { image: "my-openclaw-browser" } }, - }, - }, -} -``` - -When enabled, the agent receives: - -- 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. -Prune rules (`agents.defaults.sandbox.prune`) apply to browser containers too. - -### Custom sandbox image - -Build your own image and point config to it: - -```bash -docker build -t my-openclaw-sbx -f Dockerfile.sandbox . -``` - -```json5 -{ - agents: { - defaults: { - sandbox: { docker: { image: "my-openclaw-sbx" } }, - }, - }, -} -``` - -### 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: - -- `prune.idleHours`: remove containers not used in X hours (0 = disable) -- `prune.maxAgeDays`: remove containers older than X days (0 = disable) - -Example: - -- Keep busy sessions but cap lifetime: - `idleHours: 24`, `maxAgeDays: 7` -- Never prune: - `idleHours: 0`, `maxAgeDays: 0` - -### Security notes - -- Hard wall only applies to **tools** (exec/read/write/edit/apply_patch). -- Host-only tools like browser/camera/canvas are blocked by default. -- Allowing `browser` in sandbox **breaks isolation** (browser runs on host). - ## Troubleshooting -- 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`. -- 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). -- Custom tools not found: OpenClaw runs commands with `sh -lc` (login shell), which - 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. + + + Build the sandbox image with + [`scripts/sandbox-setup.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/sandbox-setup.sh) + or set `agents.defaults.sandbox.docker.image` to your custom image. + Containers are auto-created per session on demand. + + + + Set `docker.user` to a UID:GID that matches your mounted workspace ownership, + or chown the workspace folder. + + + + OpenClaw runs commands with `sh -lc` (login shell), which sources + `/etc/profile` and may reset PATH. Set `docker.env.PATH` to prepend your + custom tool paths, or add a script under `/etc/profile.d/` in your Dockerfile. + + + + The VM needs at least 2 GB RAM. Use a larger machine class and retry. + + + + 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 + ``` + + More detail: [Dashboard](/web/dashboard), [Devices](/cli/devices). + + + + + Reset gateway mode and bind: + + ```bash + docker compose run --rm openclaw-cli config set gateway.mode local + docker compose run --rm openclaw-cli config set gateway.bind lan + docker compose run --rm openclaw-cli devices list --url ws://127.0.0.1:18789 + ``` + + +