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.
One container, serving the gateway and the Control UI on port 18789.
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: the image's own, which calls /healthz.
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.
The Dockerfile copies openclaw.json into the image's state directory, and
Docker seeds an empty named volume from it on first start. It holds only what
this deployment needs before anyone can log in: local gateway mode, a bind to
all interfaces, the port, and the public origin and trusted proxy range as
${VAR} references that OpenClaw resolves from the environment at load. The
image's own start-up doctor --fix keeps those 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 follow upstream's own compose. /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.
cap_drop and no-new-privileges are also upstream's; 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.