openclaw
OpenClaw: a personal AI agent gateway with a web
Control UI, chat-channel bots and browser automation. Runs the project's
official image, in its -browser variant with Chromium built in.
Laid out after upstream's Docker guide
and its docker-compose.yml: one gateway container with the same command,
path variables, three volumes, healthcheck, init, dropped capabilities and
host.docker.internal mapping, serving the gateway and Control UI on port
18789. Upstream's openclaw-cli companion service is left out; the CLI runs
inside the gateway container with docker exec.
Setup
- Set
OPENCLAW_GATEWAY_TOKEN,OPENCLAW_PUBLIC_ORIGINandOPENROUTER_API_KEY. - Map the domain to port
18789and deploy. - Open the domain and connect with the gateway token. Each new browser is
then approved once from inside the container:
node openclaw.mjs devices approve. - Pick a default model in the Control UI. The image's default is an OpenAI
model, which needs
OPENAI_API_KEY.
Health check: docker-healthcheck.js from the image, which calls /healthz,
on upstream's compose timings.
Environment
| Variable | Default | Purpose |
|---|---|---|
OPENCLAW_GATEWAY_TOKEN |
— | Control UI login and API/WebSocket key |
OPENCLAW_PUBLIC_ORIGIN |
— | Public origin, e.g. https://openclaw.example.com |
OPENCLAW_TRUSTED_PROXIES |
10.0.0.0/16 |
Range the reverse proxy connects from |
OPENROUTER_API_KEY |
empty | Model provider |
TZ |
Asia/Ho_Chi_Minh |
Timezone for logs and schedules |
Other provider keys, AWS_* |
optional | Additional model providers |
TELEGRAM_BOT_TOKEN, DISCORD_BOT_TOKEN, SLACK_* |
optional | Chat channels |
OPENCLAW_GATEWAY_TOKEN is required: the gateway binds to all interfaces, and
the token is what keeps the Control UI and API closed. Provider and channel
keys are read from the environment, so the provider set is changed by
uncommenting lines.
OPENCLAW_TRUSTED_PROXIES must cover the address Coolify's Traefik connects
from. Traefik joins each app's network with an address from the Docker
address pool, which on this host is 10.0.0.0/16 in /24 slices; the gateway
answers every proxied request from an untrusted address with 403
proxy_attribution_required.
Config
The rest of OpenClaw's settings live in openclaw.json on the state volume
and are edited in the Control UI or with node openclaw.mjs config set.
Upstream's guide finishes setup with one-off commands run before the gateway
first starts: onboard, then config set for gateway.mode and
gateway.bind. Coolify has no step that runs a command against an app's
volume before it starts, so the Dockerfile does the equivalent: it copies
openclaw.json into the image's state directory, and Docker seeds an empty
named volume from it on first start. The file holds those two settings, the
port, and the two reverse-proxy settings from upstream's gateway reference,
publicOrigin and trustedProxies, as ${VAR} references that OpenClaw
resolves from the environment at load. The image's start-up doctor --fix
keeps the references when it rewrites the file. Without gateway.mode a
fresh volume crash-loops.
The seed applies only to a fresh volume. Editing openclaw.json in the
repository later changes nothing for an existing deployment; change the live
config instead.
Storage
| Volume | Mount | Holds |
|---|---|---|
openclaw-state |
/home/node/.openclaw |
openclaw.json, sessions, credentials, agent state |
openclaw-workspace |
/home/node/.openclaw/workspace |
Files the agent works on |
openclaw-secrets |
/home/node/.config/openclaw |
Auth-profile secrets |
The three mounts are upstream's own. /home/node as a whole is not mounted:
the bundled Chromium lives in /home/node/.cache, and a volume there would
freeze it at the first image's version. The image runs as the non-root node
user.
Image
ghcr.io/openclaw/openclaw:latest-browser is the latest stable release with
Playwright Chromium built in, published by the project's release automation.
Upstream publishes no major tag.
On ARM64, release 2026.9.8 cannot find its bundled Chromium ("No supported browser found"); the fix is merged upstream but not yet released. Everything except browser automation works meanwhile, and the browser starts working on the first pull after a release that contains the fix, with no change here.