refactor: switch openclaw and hermes to their official images

This commit is contained in:
tiennm99 committed 2026-10-05 13:22:01 +07:00
1 parent 8d4c3d14a4
commit 0d0a48221b
10 files changed
+174 -184

No files matched your search

+11 -29
View File
@@ -2,19 +2,20 @@
#
# cp .env.example .env
# Web UI login.
# Generate a password with: openssl rand -base64 24
AUTH_USERNAME=admin
AUTH_PASSWORD=
# Token for the agent gateway. Generate one with: openssl rand -hex 32
# Gateway token: the Control UI login and the key for every API and WebSocket call.
# Generate one with: openssl rand -hex 32
OPENCLAW_GATEWAY_TOKEN=
# At least one provider key is required; OpenRouter is the active one.
# Public origin of the Control UI, scheme included, no trailing slash.
OPENCLAW_PUBLIC_ORIGIN=https://openclaw.example.com
# Address range the reverse proxy connects from, in CIDR form.
OPENCLAW_TRUSTED_PROXIES=10.0.0.0/16
# At least one model provider; OpenRouter is the active one.
OPENROUTER_API_KEY=
# Default model, e.g. openrouter/anthropic/claude-sonnet-4.5.
# OPENCLAW_PRIMARY_MODEL=
TZ=Asia/Ho_Chi_Minh
# Other providers. Uncomment here and in compose.yml to enable one.
# ANTHROPIC_API_KEY=
@@ -26,9 +27,7 @@ OPENROUTER_API_KEY=
# CEREBRAS_API_KEY=
# VENICE_API_KEY=
# MOONSHOT_API_KEY=
# MOONSHOT_BASE_URL=https://api.moonshot.ai/v1
# KIMI_API_KEY=
# KIMI_BASE_URL=https://api.moonshot.ai/anthropic
# MINIMAX_API_KEY=
# ZAI_API_KEY=
# AI_GATEWAY_API_KEY=
@@ -36,6 +35,7 @@ OPENROUTER_API_KEY=
# SYNTHETIC_API_KEY=
# COPILOT_GITHUB_TOKEN=
# XIAOMI_API_KEY=
# OLLAMA_API_KEY=
# Speech to text.
# DEEPGRAM_API_KEY=
# AWS Bedrock.
@@ -43,27 +43,9 @@ OPENROUTER_API_KEY=
# AWS_SECRET_ACCESS_KEY=
# AWS_SESSION_TOKEN=
# AWS_REGION=us-east-1
# BEDROCK_PROVIDER_FILTER=anthropic
# OLLAMA_BASE_URL=
# Chat channels.
# TELEGRAM_BOT_TOKEN=
# DISCORD_BOT_TOKEN=
# SLACK_BOT_TOKEN=
# SLACK_APP_TOKEN=
# WHATSAPP_ENABLED=
# Gateway, browser and hook tuning.
# Origins allowed to open the control UI, comma separated, e.g. https://openclaw.example.com.
# OPENCLAW_ALLOWED_ORIGINS=
# OPENCLAW_GATEWAY_BIND=loopback
# BROWSER_DEFAULT_PROFILE=openclaw
# BROWSER_EVALUATE_ENABLED=true
# BROWSER_SNAPSHOT_MODE=efficient
# BROWSER_REMOTE_TIMEOUT_MS=1500
# BROWSER_REMOTE_HANDSHAKE_TIMEOUT_MS=3000
# HOOKS_ENABLED=false
# HOOKS_PATH=/hooks
# Extra apt packages installed into the container at start, space separated.
# OPENCLAW_DOCKER_APT_PACKAGES=
+4
View File
@@ -0,0 +1,4 @@
FROM ghcr.io/openclaw/openclaw:latest-browser
# Gateway config seeded into the state volume on first start.
COPY --chown=node:node openclaw.json /home/node/.openclaw/openclaw.json
+60 -39
View File
@@ -1,65 +1,86 @@
# openclaw
[OpenClaw](https://docs.openclaw.ai): a personal AI agent gateway with a web
UI, chat-channel bots and browser automation. Runs Coolify's
`coollabsio/openclaw` image, which wraps OpenClaw with nginx basic auth and
env-driven configuration.
Control UI, chat-channel bots and browser automation. Runs the project's
official image, in its `-browser` variant with Chromium built in.
Two containers: `openclaw`, and `browser`, a Chromium the agent drives over
the Chrome DevTools Protocol.
One container, serving the gateway and the Control UI on port `18789`.
## Setup
1. Set `AUTH_PASSWORD`, `OPENCLAW_GATEWAY_TOKEN` and `OPENROUTER_API_KEY`.
2. Map the domain to port `8080` and deploy.
3. Open the domain and log in with `AUTH_USERNAME` / `AUTH_PASSWORD`.
1. Set `OPENCLAW_GATEWAY_TOKEN`, `OPENCLAW_PUBLIC_ORIGIN` and
`OPENROUTER_API_KEY`.
2. Map the domain to port `18789` and deploy.
3. 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`.
4. Pick a default model in the Control UI. The image's default is an OpenAI
model, which needs `OPENAI_API_KEY`.
Health check: `GET /healthz`, also the compose healthcheck.
Health check: the image's own, which calls `/healthz`.
## Environment
| Variable | Default | Purpose |
| --- | --- | --- |
| `AUTH_USERNAME` / `AUTH_PASSWORD` | `admin` / — | Basic-auth login in front of the web UI |
| `OPENCLAW_GATEWAY_TOKEN` | — | Token the web UI and clients use to reach the gateway |
| `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 |
| `OPENCLAW_PRIMARY_MODEL` | optional | Default model |
| Other provider keys, `AWS_*`, `OLLAMA_BASE_URL` | optional | Additional model providers |
| `TELEGRAM_BOT_TOKEN`, `DISCORD_BOT_TOKEN`, `SLACK_*`, `WHATSAPP_ENABLED` | optional | Chat channels |
| `OPENCLAW_ALLOWED_ORIGINS` | optional | Origins allowed to open the control UI |
| `BROWSER_*`, `HOOKS_*`, `OPENCLAW_GATEWAY_BIND` | optional | Tuning, defaults shown in `compose.yml` |
| `OPENCLAW_DOCKER_APT_PACKAGES` | optional | Extra apt packages installed at start |
| `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 |
`AUTH_PASSWORD` is required: without it nginx serves the UI with no login.
The entrypoint itself refuses to start without `OPENCLAW_GATEWAY_TOKEN` or
without at least one provider key.
`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.
OpenRouter stays active as the one provider every deployment needs; any
other provider is enabled by uncommenting its line. Each provider key is read
from the environment on every start, never stored in the config.
`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`.
`PORT`, the gateway port, the state and workspace directories and
`BROWSER_CDP_URL` are fixed in `compose.yml`, as properties of this layout.
## Config
`OPENCLAW_CONFIG_JSON` sets `gateway.trustedProxies` to `127.0.0.1`. The
image's own nginx sits in front of the gateway on loopback, and current
OpenClaw (tested on 2026.9.8) rejects every proxied request with 403
`proxy_attribution_required` unless that proxy is trusted. The wrapper merges
this JSON into `openclaw.json` on every start.
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 wrapper adds the deployment's domain to the control UI's allowed origins
from the `COOLIFY_URL` and `COOLIFY_FQDN` Coolify injects.
`OPENCLAW_ALLOWED_ORIGINS` is for any other origin, and for a deployment
Coolify does not inject that into.
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-data` | `/data` | Config and sessions in `.openclaw`, the agent workspace in `workspace` |
| `browser-data` | `/config` | Chromium profile: cookies and logins of sites the agent uses |
| `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 |
## Images
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.
Both images use `latest`. `coollabsio/openclaw` publishes no major tag, only
dated releases, so `latest` is the moving one.
`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.
+16 -54
View File
@@ -1,19 +1,13 @@
services:
openclaw:
image: coollabsio/openclaw:latest
build: .
restart: unless-stopped
environment:
- AUTH_USERNAME=${AUTH_USERNAME:-admin}
- AUTH_PASSWORD=${AUTH_PASSWORD:?required}
- OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN:?required}
- OPENCLAW_PUBLIC_ORIGIN=${OPENCLAW_PUBLIC_ORIGIN:?required}
- OPENCLAW_TRUSTED_PROXIES=${OPENCLAW_TRUSTED_PROXIES:-10.0.0.0/16}
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
- PORT=8080
- OPENCLAW_GATEWAY_PORT=18789
- OPENCLAW_STATE_DIR=/data/.openclaw
- OPENCLAW_WORKSPACE_DIR=/data/workspace
- BROWSER_CDP_URL=http://browser:9223
- 'OPENCLAW_CONFIG_JSON={"gateway":{"trustedProxies":["127.0.0.1"]}}'
# - OPENCLAW_PRIMARY_MODEL=${OPENCLAW_PRIMARY_MODEL:-}
- TZ=${TZ:-Asia/Ho_Chi_Minh}
# - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
# - OPENAI_API_KEY=${OPENAI_API_KEY:-}
# - GEMINI_API_KEY=${GEMINI_API_KEY:-}
@@ -23,9 +17,7 @@ services:
# - CEREBRAS_API_KEY=${CEREBRAS_API_KEY:-}
# - VENICE_API_KEY=${VENICE_API_KEY:-}
# - MOONSHOT_API_KEY=${MOONSHOT_API_KEY:-}
# - MOONSHOT_BASE_URL=${MOONSHOT_BASE_URL:-https://api.moonshot.ai/v1}
# - KIMI_API_KEY=${KIMI_API_KEY:-}
# - KIMI_BASE_URL=${KIMI_BASE_URL:-https://api.moonshot.ai/anthropic}
# - MINIMAX_API_KEY=${MINIMAX_API_KEY:-}
# - ZAI_API_KEY=${ZAI_API_KEY:-}
# - AI_GATEWAY_API_KEY=${AI_GATEWAY_API_KEY:-}
@@ -33,57 +25,27 @@ services:
# - SYNTHETIC_API_KEY=${SYNTHETIC_API_KEY:-}
# - COPILOT_GITHUB_TOKEN=${COPILOT_GITHUB_TOKEN:-}
# - XIAOMI_API_KEY=${XIAOMI_API_KEY:-}
# - OLLAMA_API_KEY=${OLLAMA_API_KEY:-}
# - DEEPGRAM_API_KEY=${DEEPGRAM_API_KEY:-}
# - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-}
# - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}
# - AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN:-}
# - AWS_REGION=${AWS_REGION:-us-east-1}
# - BEDROCK_PROVIDER_FILTER=${BEDROCK_PROVIDER_FILTER:-anthropic}
# - OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-}
# - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-}
# - DISCORD_BOT_TOKEN=${DISCORD_BOT_TOKEN:-}
# - SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN:-}
# - SLACK_APP_TOKEN=${SLACK_APP_TOKEN:-}
# - WHATSAPP_ENABLED=${WHATSAPP_ENABLED:-}
# - OPENCLAW_ALLOWED_ORIGINS=${OPENCLAW_ALLOWED_ORIGINS:-}
# - OPENCLAW_GATEWAY_BIND=${OPENCLAW_GATEWAY_BIND:-loopback}
# - BROWSER_DEFAULT_PROFILE=${BROWSER_DEFAULT_PROFILE:-openclaw}
# - BROWSER_EVALUATE_ENABLED=${BROWSER_EVALUATE_ENABLED:-true}
# - BROWSER_SNAPSHOT_MODE=${BROWSER_SNAPSHOT_MODE:-efficient}
# - BROWSER_REMOTE_TIMEOUT_MS=${BROWSER_REMOTE_TIMEOUT_MS:-1500}
# - BROWSER_REMOTE_HANDSHAKE_TIMEOUT_MS=${BROWSER_REMOTE_HANDSHAKE_TIMEOUT_MS:-3000}
# - HOOKS_ENABLED=${HOOKS_ENABLED:-false}
# - HOOKS_PATH=${HOOKS_PATH:-/hooks}
# - OPENCLAW_DOCKER_APT_PACKAGES=${OPENCLAW_DOCKER_APT_PACKAGES:-}
volumes:
- openclaw-data:/data
depends_on:
browser:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-sf", "http://127.0.0.1:8080/healthz"]
interval: 10s
timeout: 10s
retries: 5
# Chromium the agent drives over CDP.
browser:
image: coollabsio/openclaw-browser:latest
restart: unless-stopped
shm_size: 2g
environment:
- PUID=1000
- PGID=1000
- CHROME_CLI=--remote-debugging-port=9222
- TZ=Etc/UTC
volumes:
- browser-data:/config
healthcheck:
test: ["CMD-SHELL", "bash -c ':> /dev/tcp/127.0.0.1/9222' || exit 1"]
interval: 5s
timeout: 5s
retries: 10
- openclaw-state:/home/node/.openclaw
- openclaw-workspace:/home/node/.openclaw/workspace
- openclaw-secrets:/home/node/.config/openclaw
cap_drop:
- NET_RAW
- NET_ADMIN
security_opt:
- no-new-privileges:true
volumes:
openclaw-data:
browser-data:
openclaw-state:
openclaw-workspace:
openclaw-secrets:
+9
View File
@@ -0,0 +1,9 @@
{
"gateway": {
"mode": "local",
"bind": "lan",
"port": 18789,
"publicOrigin": "${OPENCLAW_PUBLIC_ORIGIN}",
"trustedProxies": ["${OPENCLAW_TRUSTED_PROXIES}"]
}
}