docker CLI. Set the backend to "podman" to select native Podman directly. Sandboxing is off by default and does not require the gateway itself to run in a container. SSH and OpenShell sandbox backends are also available; see Sandboxing.
Hosting multiple users? See Multi-tenant hosting for the one-cell-per-tenant model.
Prerequisites
- Docker Desktop (or Docker Engine) + Docker Compose v2
- At least 2 GB RAM for image build (
pnpm installmay be OOM-killed on 1 GB hosts with exit 137) - Enough disk for images and logs
- On a VPS/public host, review Security hardening for network exposure, especially the Docker
DOCKER-USERfirewall chain
Containerized gateway
1
Build the image
From the repo root:This builds the gateway image locally as Pre-built images are published first to the GitHub Container Registry. GHCR is the primary registry for release automation, pinned deployments, and provenance checks. The same release publishes a Docker Hub mirror at Use
openclaw:local. To use a pre-built image instead:openclaw/openclaw:ghcr.io/openclaw/openclaw or openclaw/openclaw and avoid unofficial mirrors, which don’t share OpenClaw’s release timing or retention policy. Version-specific tags include releases such as 2026.2.26 and prereleases such as 2026.2.26-beta.1. Stable releases move latest and main; trailing-month Gateway releases move only extended-stable. Variants include slim, main-slim, extended-stable-slim, latest-browser, main-browser, and extended-stable-browser. The default images bundle the codex and diagnostics-otel plugins. A -browser variant also ships with Chromium baked in, useful for the sandboxed browser tool without a first-run Playwright install.2
Airgapped rerun
On offline hosts, transfer and load the image first:
--offline verifies OPENCLAW_IMAGE already exists locally, disables implicit Compose pulls/builds, then runs the normal flow: .env sync, permission fixes, onboarding, gateway config sync, Compose startup.If OPENCLAW_SANDBOX=1, offline setup also checks the configured default and per-agent sandbox images on the daemon behind OPENCLAW_DOCKER_SOCKET, including the browser-contract label on Docker-backed browser images. If a required image is missing or stale, setup exits without changing sandbox config rather than reporting a broken success.3
Complete onboarding
The setup script runs onboarding automatically:
- prompts for provider API keys
- generates a gateway token and writes it to
.env - creates the auth-profile secret key directory
- starts the gateway via Docker Compose
openclaw-gateway directly (with --no-deps --entrypoint node), since openclaw-cli shares the gateway’s network namespace and only works once the gateway container exists.4
Open the Control UI
Open
http://127.0.0.1:18789/ and paste the token written to .env into Settings. If you switched the container to password auth, use that password instead.Need the URL again?Headless bootstrap
For an unattended container host, put provider, Gateway, and channel credentials in the Compose.env file so both the one-shot bootstrap container and the long-running Gateway receive the same values:
TELEGRAM_BOT_TOKEN in .env after bootstrap: --use-env leaves credential lookup to the environment without copying the token into openclaw.json, and the running Gateway needs the same variable. When channel config changes after startup, the Gateway’s config watcher hot-reloads the affected channel automatically.
See openclaw channels for credential-flag alternatives and other channel plugins.
Manual flow
.git. Pass the source identity as build arguments
as shown above so the image’s About screen reports the checked-out commit and
one build timestamp. scripts/docker/setup.sh resolves and passes both values
automatically.
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 after any docker-compose.override.yml you maintain yourself, e.g. -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.extra.yml.Upgrading container images
When you replace the OpenClaw image but keep the same mounted state/config, the new gateway runs startup-safe upgrade migrations and plugin convergence before readiness. Routine image upgrades should not require a separateopenclaw doctor --fix pass.
If startup cannot complete those repairs safely, the gateway exits instead of
reporting healthy. With a restart policy, Docker, Podman, or Kubernetes may show
the gateway container restarting. Keep the mounted state volume, then run the
same image once with openclaw doctor --fix as the container command, using the
same state/config mounts the gateway uses:
Environment variables
Optional variables accepted byscripts/docker/setup.sh (and, for the gateway container, by docker-compose.yml directly):
The official image ships no Homebrew. During onboarding, OpenClaw hides brew-only skill dependency installers in a Linux container without
brew; provide those dependencies through a custom image or install manually. Use OPENCLAW_IMAGE_APT_PACKAGES for Debian-packaged dependencies and OPENCLAW_IMAGE_PIP_PACKAGES for Python dependencies (runs python3 -m pip install --break-system-packages at build time, so pin versions and use only indexes you trust).
If Docker reports ResourceExhausted, cannot allocate memory, or aborts during tsdown, increase the Docker builder memory limit or retry with smaller explicit heaps:
Source-built images with selected plugins
OPENCLAW_EXTENSIONS selects plugin manifest ids from the source checkout;
existing source-directory names are also accepted when they differ. The Docker
build resolves the selection to source directories once, installs production
dependencies, and includes the selected plugin runtime in the image. Source
checkouts also compile first-party plugins published separately with
openclaw.build.bundledDist: false; that marker still preserves the plugin’s
external npm or ClawHub ownership and does not change either artifact contract.
Unknown, invalid, or ambiguous ids fail the image build.
Known dependency/source-only ids keep their existing source and dependency
staging without gaining a compiled root dist entry. A selected plugin with
unified build entries must compile successfully; unselected external plugin
source and runtime output are pruned.
For example, these commands build separate, multi-architecture standalone
FakeCo gateway images for ClickClack, Slack, and Microsoft Teams. ClawRouter is
already part of the root OpenClaw runtime, so the ClickClack image selects only
clickclack. The explicit empty browser argument keeps the default image free
of Chromium:
--platform linux/arm64 --load or --platform linux/amd64 --load for a
single native local build. Multi-platform output and attached SBOM/provenance
require a registry or another Buildx output that preserves attestations. After
pushing, inspect the manifest and deploy the immutable digest rather than the
mutable source-SHA tag:
OPENCLAW_EXTRA_MOUNTS=/path/to/fork/extensions/synology-chat:/app/extensions/synology-chat:ro. That overrides the matching compiled /app/dist/extensions/synology-chat bundle for the same plugin id.
Observability
OpenTelemetry export is outbound from the Gateway container to your OTLP collector; it needs no published Docker port. To include the bundled exporter in a locally built image:diagnostics-otel; install clawhub:@openclaw/diagnostics-otel yourself only if you removed it. To enable export, allow and enable the diagnostics-otel plugin in config, then set diagnostics.otel.enabled=true (see the full example in OpenTelemetry export). Collector auth headers go through diagnostics.otel.headers, not Docker environment variables.
Prometheus metrics reuse the already-published Gateway port. Install clawhub:@openclaw/diagnostics-prometheus, enable the diagnostics-prometheus plugin, then scrape:
/metrics port or unauthenticated reverse-proxy path. See Prometheus metrics.
Health checks
Container probe endpoints (no auth required):HEALTHCHECK pings /healthz; repeated failures mark the container unhealthy so orchestrators can restart or replace it.
Use /startupz for an orchestrator startup or readiness probe so a failed channel account does not remove the otherwise healthy Gateway and Control UI from service. Use /readyz for monitoring that intentionally treats hard channel failures as not ready. See Health checks for response details.
Authenticated deep health snapshot:
LAN vs loopback
scripts/docker/setup.sh defaults OPENCLAW_GATEWAY_BIND=lan so http://127.0.0.1:18789 on the host works with Docker port publishing.
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.
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.Host local providers
Inside the container,127.0.0.1 is the container itself, not the host. Use host.docker.internal for providers running on the host:
The bundled setup uses those URLs as LM Studio/Ollama onboarding defaults, and
docker-compose.yml maps host.docker.internal to the host gateway on Linux Docker Engine (Docker Desktop provides the same alias on macOS/Windows). Host services must listen on an address Docker can reach:
docker run? Add the same mapping yourself, e.g. --add-host=host.docker.internal:host-gateway.
Claude CLI backend in Docker
The official image does not pre-install Claude Code. Install and log in inside the container’snode user, then persist that container home so image upgrades don’t erase the binary or auth state.
For a new install, enable a persistent /home/node volume before running setup:
.env values first — the setup script always rewrites .env from the current shell and defaults, it doesn’t read the file on its own:
.env contains values your shell can’t source, re-export what you rely on manually first (OPENCLAW_IMAGE, ports, bind mode, custom paths, OPENCLAW_EXTRA_MOUNTS, sandbox, skip-onboarding). The generated overlay mounts the home volume for both openclaw-gateway and openclaw-cli; run the remaining commands with that overlay (and docker-compose.override.yml first, if you use one):
claude to /home/node/.local/bin/claude. The
OpenClaw image includes /home/node/.local/bin on PATH, so the bundled
Anthropic plugin resolves it without an adapter config override.
Log in and verify from the same persisted home:
claude-cli backend:
OPENCLAW_HOME_VOLUME persists the native install under /home/node/.local/bin and /home/node/.local/share/claude, plus Claude Code settings/auth under /home/node/.claude and /home/node/.claude.json. Persisting only /home/node/.openclaw is not enough; if you use OPENCLAW_EXTRA_MOUNTS instead of a home volume, mount all of those Claude paths into both services.
For shared production automation or predictable Anthropic billing, prefer the Anthropic API-key path. Claude CLI reuse follows Claude Code’s installed version, account login, billing, and update behavior.
Bonjour / mDNS
Docker bridge networking usually doesn’t forward Bonjour/mDNS multicast (224.0.0.251:5353) reliably. When OPENCLAW_DISABLE_BONJOUR is unset, the bundled Bonjour plugin auto-disables LAN advertising once it detects it’s running in a container, so it won’t crash-loop retrying multicast the bridge drops. Set OPENCLAW_DISABLE_BONJOUR=1 to force it off regardless of detection, or 0 to force it on (only on host networking, macvlan, or another network where mDNS multicast is known to work).
Use the published Gateway URL, Tailscale, or wide-area DNS-SD for Docker hosts otherwise. See Bonjour discovery for gotchas and troubleshooting.
Storage and persistence
Docker Compose bind-mountsOPENCLAW_CONFIG_DIR to /home/node/.openclaw, OPENCLAW_WORKSPACE_DIR to /home/node/.openclaw/workspace, and OPENCLAW_AUTH_PROFILE_SECRET_DIR to /home/node/.config/openclaw, so those paths survive container replacement. When a variable is unset, docker-compose.yml falls back under ${HOME}, or /tmp if HOME itself is missing, so docker compose up never emits an empty-source volume spec on bare environments.
That mounted config directory holds:
openclaw.jsonfor behavior configagents/<agentId>/agent/auth-profiles.jsonfor stored provider OAuth/API-key auth.envfor env-backed runtime secrets such asOPENCLAW_GATEWAY_TOKEN
OPENCLAW_CONFIG_DIR.
Installed downloadable plugins store package state under the mounted OpenClaw home, so install records and package roots survive container replacement; gateway startup does not regenerate bundled-plugin dependency trees.
For full VM persistence details, see Docker VM Runtime - What persists where.
Disk growth hotspots: media/, per-agent SQLite databases, legacy session JSONL transcripts, the shared SQLite state database, installed plugin package roots, and rolling file logs under /tmp/openclaw/.
Shell helpers (optional)
For shorter day-to-day commands, install ClawDock:scripts/shell-helpers/clawdock-helpers.sh path, rerun the command above so your local helper tracks the current location. Then use clawdock-start, clawdock-stop, clawdock-dashboard, etc. (run clawdock-help for the full list).
Enable agent sandbox for Docker gateway
Enable agent sandbox for Docker gateway
docker.sock only after sandbox prerequisites pass. If sandbox setup can’t complete, it resets agents.defaults.sandbox.mode to off. Codex code mode is disabled for turns where the OpenClaw sandbox is active (see Sandboxing § Docker backend); never mount the host Docker socket into agent sandbox containers.Automation / CI (non-interactive)
Automation / CI (non-interactive)
Disable Compose pseudo-TTY allocation with
-T:Docker Desktop DNS failures in openclaw-cli
Docker Desktop DNS failures in openclaw-cli
Some Docker Desktop setups fail DNS lookups from the shared-network If you already created a long-running
openclaw-cli sidecar after NET_RAW is dropped, showing up as EAI_AGAIN during npm-backed commands like openclaw plugins install. Keep the default hardened compose file for normal operation. The override below restores default capabilities for the openclaw-cli container only — use it for the one-off command that needs registry access, not as your default invocation:openclaw-cli container, recreate it with the same override — docker compose exec/docker exec can’t change Linux capabilities on an already-created container.Permissions and EACCES
Permissions and EACCES
The image runs as The same mismatch can show up as
node (uid 1000). If you see permission errors on /home/node/.openclaw, make sure your host bind mounts are owned by uid 1000:blocked plugin candidate: suspicious ownership (... uid=1000, expected uid=0 or root) followed by plugin present but blocked — the process uid and the mounted plugin directory owner disagree. Prefer running as the default uid 1000 and fixing the bind mount ownership. Only chown /path/to/openclaw-config/npm to root:root if you intentionally run OpenClaw as root long term.Faster rebuilds
Faster rebuilds
Use the repo-root
Dockerfile instead of replacing it with a shortened
single-stage example. Its workspace-deps stage extracts the package
manifests required by pnpm-workspace.yaml, then the build stage copies
those manifests before pnpm install --frozen-lockfile. This keeps the
dependency layer cacheable without omitting packages/*, selected
extensions/*, or other required workspace metadata.The same Dockerfile preserves the production runtime contract: digest-pinned
Node and Bun bases, non-root uid 1000, tini, the built-in health check, and
the /usr/local/bin/openclaw symlink. Dependabot refreshes the reviewed base
digests; do not replace them with floating FROM node:24-bookworm tags.Power-user container options
Power-user container options
The default image is security-first and runs as non-root
node. For a more full-featured container:- Persist
/home/node:export OPENCLAW_HOME_VOLUME="openclaw_home" - Bake system deps:
export OPENCLAW_IMAGE_APT_PACKAGES="git curl jq" - Bake Python deps:
export OPENCLAW_IMAGE_PIP_PACKAGES="requests==2.32.5 humanize==4.14.0" - Bake Playwright Chromium:
export OPENCLAW_INSTALL_BROWSER=1, or use the official-browserimage tag - Persist browser downloads and caches: use
OPENCLAW_HOME_VOLUMEorOPENCLAW_EXTRA_MOUNTS. OpenClaw auto-detects the image’s Playwright-managed Chromium on Linux.
OpenAI Codex OAuth (headless Docker)
OpenAI Codex OAuth (headless Docker)
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.
Base image metadata
Base image metadata
The runtime image uses
node:24-bookworm-slim and runs tini as PID 1 so zombie processes are reaped and signals handled correctly in long-running containers. It publishes OCI base-image annotations including org.opencontainers.image.base.name and org.opencontainers.image.source. Dependabot refreshes the pinned Node base digest, and each build applies current Debian point-release updates. See OCI image annotations.Image contents and security scanning
Runtime images contain production Node.js dependencies only. Release builds pin the base image by digest and apply current Debian security updates withapt-get dist-upgrade; the -browser variant installs the Chromium version pinned by its Playwright release.
Scanner totals can include Debian findings that the distribution marks wont-fix. To rebuild locally against current base and package metadata, run docker build --pull -t openclaw:local ..
Weekly image refreshes
Thelatest*, main*, and extended-stable* moving tags are rebuilt weekly from the same tagged release source so they pick up current OS security updates between OpenClaw releases. Stable and extended-stable refreshes remain separate, and beta images are not rebuilt on this schedule.
Each refresh also publishes a dated tag such as 2026.8.1-r20260820 (plus -slim and -browser variants). Plain version tags and dated -rYYYYMMDD tags are immutable; pin either form when you do not want a deployment to follow a moving tag.
Running on a VPS?
See Hetzner (Docker VPS) and Docker VM Runtime for shared VM deployment steps including binary baking, persistence, and updates.Agent sandbox
Whenagents.defaults.sandbox is enabled with the Docker backend, the gateway runs agent tool execution (shell, file read/write, etc.) inside isolated Docker containers while the gateway itself stays on the host — a hard wall around untrusted or multi-tenant agent sessions without containerizing the whole 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:
- Sandboxing — complete sandbox reference
- OpenShell — interactive shell access to sandbox containers
- Multi-Agent Sandbox and Tools — per-agent overrides
Quick enable
docker build commands.
Troubleshooting
Image missing or sandbox container not starting
Image missing or sandbox container not starting
Build the sandbox image with
scripts/sandbox-setup.sh (source checkout) or the inline docker build command from Sandboxing § Images and setup (npm install), or set agents.defaults.sandbox.docker.image to your custom image. Containers are auto-created per session on demand.Permission errors in sandbox
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 in sandbox
Custom tools not found in sandbox
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.OOM-killed during image build (exit 137)
OOM-killed during image build (exit 137)
The VM needs at least 2 GB RAM. Use a larger machine class and retry.
Gateway target shows ws://172.x.x.x or pairing errors from Docker CLI
Gateway target shows ws://172.x.x.x or pairing errors from Docker CLI
Reset gateway mode and bind:
Related
- Install Overview — all installation methods
- Podman — Podman alternative to Docker
- ClawDock — Docker Compose community setup
- Updating — keeping OpenClaw up to date
- Configuration — gateway configuration after install