mirror of
https://github.com/tiennm99/composes.git
synced 2026-10-11 03:13:16 +00:00
refactor: align openclaw and hermes with their official Docker guides
This commit is contained in:
1 parent
77c12d5397
commit
1cbbf271ce
6 files changed
+80
-73
No files matched your search
+3
-4
@@ -11,16 +11,15 @@ HERMES_DASHBOARD_PASSWORD=
|
|||||||
# Signs dashboard sessions; keep it stable. Generate with: openssl rand -hex 32
|
# Signs dashboard sessions; keep it stable. Generate with: openssl rand -hex 32
|
||||||
HERMES_DASHBOARD_SECRET=
|
HERMES_DASHBOARD_SECRET=
|
||||||
|
|
||||||
# Model provider for the agent.
|
|
||||||
OPENROUTER_API_KEY=
|
|
||||||
|
|
||||||
# Telegram gateway: bot token, then comma-separated user ids and group chat ids
|
# Telegram gateway: bot token, then comma-separated user ids and group chat ids
|
||||||
# allowed to talk to it. An empty token leaves Telegram off.
|
# allowed to talk to it. An empty token leaves Telegram off.
|
||||||
TELEGRAM_BOT_TOKEN=
|
TELEGRAM_BOT_TOKEN=
|
||||||
TELEGRAM_ALLOWED_USERS=
|
TELEGRAM_ALLOWED_USERS=
|
||||||
TELEGRAM_GROUP_ALLOWED_CHATS=
|
TELEGRAM_GROUP_ALLOWED_CHATS=
|
||||||
|
|
||||||
# Other providers. Uncomment here and in compose.yml to enable one.
|
# Provider keys normally live in /opt/data/.env, set from the dashboard.
|
||||||
|
# Uncomment here and in compose.yml to pass one from the environment instead.
|
||||||
|
# OPENROUTER_API_KEY=
|
||||||
# ANTHROPIC_API_KEY=
|
# ANTHROPIC_API_KEY=
|
||||||
# OPENAI_API_KEY=
|
# OPENAI_API_KEY=
|
||||||
# GOOGLE_API_KEY=
|
# GOOGLE_API_KEY=
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
FROM nousresearch/hermes-agent:latest
|
|
||||||
|
|
||||||
# Workspace directory owned by the hermes user (HERMES_UID/HERMES_GID 1000).
|
|
||||||
RUN mkdir -p /workspace && chown 1000:1000 /workspace
|
|
||||||
+29
-36
@@ -4,19 +4,19 @@
|
|||||||
agent with persistent memory, scheduling and chat-platform gateways, from Nous
|
agent with persistent memory, scheduling and chat-platform gateways, from Nous
|
||||||
Research's official image, with its built-in web dashboard.
|
Research's official image, with its built-in web dashboard.
|
||||||
|
|
||||||
One container. `gateway run` starts the agent gateway, and the image's s6
|
Laid out after upstream's
|
||||||
supervisor also starts the dashboard on port `9119`: chat (the Hermes
|
[Docker guide](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/docker.md):
|
||||||
terminal UI in the browser), sessions, config, cron, skills and logs.
|
one container, one data volume at `/opt/data`, `gateway run` as the command,
|
||||||
|
and `HERMES_DASHBOARD=1` so the image's s6 supervisor also runs the dashboard
|
||||||
|
on port `9119`.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. Set `HERMES_DASHBOARD_PUBLIC_URL`, `HERMES_DASHBOARD_PASSWORD`,
|
1. Set `HERMES_DASHBOARD_PUBLIC_URL`, `HERMES_DASHBOARD_PASSWORD` and
|
||||||
`HERMES_DASHBOARD_SECRET` and `OPENROUTER_API_KEY`.
|
`HERMES_DASHBOARD_SECRET`.
|
||||||
2. Map the domain to port `9119` and deploy.
|
2. Map the domain to port `9119` and deploy.
|
||||||
3. Open the domain and log in with `HERMES_DASHBOARD_USERNAME` /
|
3. Open the domain, log in with `HERMES_DASHBOARD_USERNAME` /
|
||||||
`HERMES_DASHBOARD_PASSWORD`.
|
`HERMES_DASHBOARD_PASSWORD`, and add a model provider under Config.
|
||||||
|
|
||||||
Health check: `GET /api/status`, also the compose healthcheck.
|
|
||||||
|
|
||||||
## Environment
|
## Environment
|
||||||
|
|
||||||
@@ -25,50 +25,43 @@ Health check: `GET /api/status`, also the compose healthcheck.
|
|||||||
| `HERMES_DASHBOARD_PUBLIC_URL` | — | Full public URL, e.g. `https://hermes.example.com` |
|
| `HERMES_DASHBOARD_PUBLIC_URL` | — | Full public URL, e.g. `https://hermes.example.com` |
|
||||||
| `HERMES_DASHBOARD_USERNAME` / `HERMES_DASHBOARD_PASSWORD` | `hermes` / — | Dashboard login |
|
| `HERMES_DASHBOARD_USERNAME` / `HERMES_DASHBOARD_PASSWORD` | `hermes` / — | Dashboard login |
|
||||||
| `HERMES_DASHBOARD_SECRET` | — | Signs dashboard sessions |
|
| `HERMES_DASHBOARD_SECRET` | — | Signs dashboard sessions |
|
||||||
| `OPENROUTER_API_KEY` | empty | Model provider |
|
|
||||||
| `TELEGRAM_BOT_TOKEN` | empty | Telegram bot; empty leaves Telegram off |
|
| `TELEGRAM_BOT_TOKEN` | empty | Telegram bot; empty leaves Telegram off |
|
||||||
| `TELEGRAM_ALLOWED_USERS` / `TELEGRAM_GROUP_ALLOWED_CHATS` | empty | Telegram users and group chats allowed to use the bot |
|
| `TELEGRAM_ALLOWED_USERS` / `TELEGRAM_GROUP_ALLOWED_CHATS` | empty | Telegram users and group chats allowed to use the bot |
|
||||||
| `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY` | optional | Other model providers |
|
| `OPENROUTER_API_KEY`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY` | optional | Provider keys passed from the environment |
|
||||||
|
|
||||||
`HERMES_DASHBOARD=1` turns the dashboard on. Bound to `0.0.0.0`, it refuses to
|
The dashboard refuses to start on a non-loopback bind without an auth
|
||||||
start without an auth provider, so the password is required. The image's
|
provider, so the password is required. The username/password provider is the
|
||||||
username/password provider is the one that needs no outside identity service;
|
one that needs no outside identity service; upstream describes it as meant
|
||||||
upstream describes it as meant for trusted networks and recommends OAuth (Nous
|
for trusted networks and recommends OAuth (Nous Portal) or self-hosted OIDC
|
||||||
Portal) or self-hosted OIDC for a public domain.
|
for a public domain.
|
||||||
|
|
||||||
`HERMES_DASHBOARD_PUBLIC_URL` adds the domain to the dashboard's Host and
|
`HERMES_DASHBOARD_PUBLIC_URL` adds the domain to the dashboard's Host and
|
||||||
WebSocket Origin guard, which rejects requests for any other host.
|
WebSocket Origin guard, which rejects requests for any other host.
|
||||||
|
|
||||||
`HERMES_DASHBOARD_SECRET` keeps sessions valid across restarts; without it
|
`HERMES_DASHBOARD_SECRET` keeps sessions valid across restarts; without it
|
||||||
each restart signs with a new random key and logs everyone out.
|
each restart signs with a new random key.
|
||||||
|
|
||||||
The uid/gid are pinned to `1000` in `compose.yml`; the `Dockerfile` depends on
|
Provider keys are commented out because upstream keeps them in
|
||||||
that (see Storage).
|
`/opt/data/.env`, written from the dashboard, so they survive without being
|
||||||
|
repeated in Coolify. An environment variable, when set, wins over that file.
|
||||||
|
|
||||||
## Storage
|
## Storage
|
||||||
|
|
||||||
| Volume | Mount | Holds |
|
| Volume | Mount | Holds |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `hermes-data` | `/opt/data` | `HERMES_HOME`: config, `.env`, sessions, memory, skills, logs |
|
| `hermes-data` | `/opt/data` | `HERMES_HOME`: config, `.env`, sessions, memory, skills, logs, and the agent's working files |
|
||||||
| `hermes-workspace` | `/workspace` | Files the agent works on |
|
|
||||||
|
|
||||||
The image hard-blocks the agent's file tools from writing outside
|
`/opt/data` is the image's own home for all mutable state: it is the `hermes`
|
||||||
`HERMES_WRITE_SAFE_ROOT`, which it sets to `/opt/data` alone, so
|
user's home directory, the image declares it a volume, and the dashboard's
|
||||||
`compose.yml` adds `/workspace` to it. `TERMINAL_CWD` starts gateway and cron
|
start script uses it directly. The agent's file tools may only write under
|
||||||
terminal sessions in `/workspace`. Upstream marks that variable deprecated in
|
it. The `hermes` user keeps the image's default uid, `10000`, and the image fixes
|
||||||
favour of `terminal.cwd` in `config.yaml`; it is used here so the setting stays
|
the volume's ownership for that user at start.
|
||||||
in the compose file rather than on the volume.
|
|
||||||
|
|
||||||
The `Dockerfile` exists only to make `/workspace` writable. The image does not
|
## Resources
|
||||||
ship that directory and its init chowns only `/opt/data`, so a named volume on
|
|
||||||
`/workspace` would come up `root:root`. Creating the directory in the image,
|
The 4 GB memory and 2 CPU limits are upstream's own compose example.
|
||||||
owned by `1000:1000`, fixes that: Docker seeds an empty named volume from the
|
|
||||||
image directory, ownership included.
|
|
||||||
|
|
||||||
## Image
|
## Image
|
||||||
|
|
||||||
`nousresearch/hermes-agent:latest` moves only on stable releases, roughly
|
`nousresearch/hermes-agent:latest` moves only on stable releases, roughly
|
||||||
weekly; upstream publishes no major tag. The agent's code lives in the image,
|
weekly; upstream publishes no major tag. The agent's code lives in the image,
|
||||||
not a volume, so a new release takes effect on the next pull and recreate. The
|
not a volume, so a new release takes effect on the next pull and recreate.
|
||||||
first start after an upgrade migrates `config.yaml`, keeping a timestamped
|
|
||||||
backup.
|
|
||||||
+7
-14
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
hermes:
|
hermes:
|
||||||
build: .
|
image: nousresearch/hermes-agent:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: gateway run
|
command: gateway run
|
||||||
environment:
|
environment:
|
||||||
@@ -9,27 +9,20 @@ services:
|
|||||||
- HERMES_DASHBOARD_BASIC_AUTH_USERNAME=${HERMES_DASHBOARD_USERNAME:-hermes}
|
- HERMES_DASHBOARD_BASIC_AUTH_USERNAME=${HERMES_DASHBOARD_USERNAME:-hermes}
|
||||||
- HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=${HERMES_DASHBOARD_PASSWORD:?required}
|
- HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=${HERMES_DASHBOARD_PASSWORD:?required}
|
||||||
- HERMES_DASHBOARD_BASIC_AUTH_SECRET=${HERMES_DASHBOARD_SECRET:?required}
|
- HERMES_DASHBOARD_BASIC_AUTH_SECRET=${HERMES_DASHBOARD_SECRET:?required}
|
||||||
- HERMES_UID=1000
|
|
||||||
- HERMES_GID=1000
|
|
||||||
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
|
|
||||||
- HERMES_WRITE_SAFE_ROOT=/opt/data:/workspace
|
|
||||||
- TERMINAL_CWD=/workspace
|
|
||||||
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-}
|
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-}
|
||||||
- TELEGRAM_ALLOWED_USERS=${TELEGRAM_ALLOWED_USERS:-}
|
- TELEGRAM_ALLOWED_USERS=${TELEGRAM_ALLOWED_USERS:-}
|
||||||
- TELEGRAM_GROUP_ALLOWED_CHATS=${TELEGRAM_GROUP_ALLOWED_CHATS:-}
|
- TELEGRAM_GROUP_ALLOWED_CHATS=${TELEGRAM_GROUP_ALLOWED_CHATS:-}
|
||||||
|
# - OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
|
||||||
# - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
# - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
||||||
# - OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
# - OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||||
# - GOOGLE_API_KEY=${GOOGLE_API_KEY:-}
|
# - GOOGLE_API_KEY=${GOOGLE_API_KEY:-}
|
||||||
volumes:
|
volumes:
|
||||||
- hermes-data:/opt/data
|
- hermes-data:/opt/data
|
||||||
- hermes-workspace:/workspace
|
deploy:
|
||||||
healthcheck:
|
resources:
|
||||||
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:9119/api/status"]
|
limits:
|
||||||
interval: 30s
|
memory: 4G
|
||||||
timeout: 5s
|
cpus: "2.0"
|
||||||
retries: 3
|
|
||||||
start_period: 60s
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
hermes-data:
|
hermes-data:
|
||||||
hermes-workspace:
|
|
||||||
+23
-15
@@ -4,7 +4,12 @@
|
|||||||
Control UI, chat-channel bots and browser automation. Runs the project's
|
Control UI, chat-channel bots and browser automation. Runs the project's
|
||||||
official image, in its `-browser` variant with Chromium built in.
|
official image, in its `-browser` variant with Chromium built in.
|
||||||
|
|
||||||
One container, serving the gateway and the Control UI on port `18789`.
|
Laid out after upstream's [Docker guide](https://docs.openclaw.ai/install/docker)
|
||||||
|
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
|
## Setup
|
||||||
|
|
||||||
@@ -17,7 +22,8 @@ One container, serving the gateway and the Control UI on port `18789`.
|
|||||||
4. Pick a default model in the Control UI. The image's default is an OpenAI
|
4. Pick a default model in the Control UI. The image's default is an OpenAI
|
||||||
model, which needs `OPENAI_API_KEY`.
|
model, which needs `OPENAI_API_KEY`.
|
||||||
|
|
||||||
Health check: the image's own, which calls `/healthz`.
|
Health check: `docker-healthcheck.js` from the image, which calls `/healthz`,
|
||||||
|
on upstream's compose timings.
|
||||||
|
|
||||||
## Environment
|
## Environment
|
||||||
|
|
||||||
@@ -47,13 +53,17 @@ answers every proxied request from an untrusted address with 403
|
|||||||
The rest of OpenClaw's settings live in `openclaw.json` on the state volume
|
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`.
|
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
|
Upstream's guide finishes setup with one-off commands run before the gateway
|
||||||
Docker seeds an empty named volume from it on first start. It holds only what
|
first starts: `onboard`, then `config set` for `gateway.mode` and
|
||||||
this deployment needs before anyone can log in: local gateway mode, a bind to
|
`gateway.bind`. Coolify has no step that runs a command against an app's
|
||||||
all interfaces, the port, and the public origin and trusted proxy range as
|
volume before it starts, so the `Dockerfile` does the equivalent: it copies
|
||||||
`${VAR}` references that OpenClaw resolves from the environment at load. The
|
`openclaw.json` into the image's state directory, and Docker seeds an empty
|
||||||
image's own start-up `doctor --fix` keeps those references when it rewrites
|
named volume from it on first start. The file holds those two settings, the
|
||||||
the file. Without `gateway.mode` a fresh volume crash-loops.
|
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
|
The seed applies only to a fresh volume. Editing `openclaw.json` in the
|
||||||
repository later changes nothing for an existing deployment; change the live
|
repository later changes nothing for an existing deployment; change the live
|
||||||
@@ -67,12 +77,10 @@ config instead.
|
|||||||
| `openclaw-workspace` | `/home/node/.openclaw/workspace` | Files the agent works on |
|
| `openclaw-workspace` | `/home/node/.openclaw/workspace` | Files the agent works on |
|
||||||
| `openclaw-secrets` | `/home/node/.config/openclaw` | Auth-profile secrets |
|
| `openclaw-secrets` | `/home/node/.config/openclaw` | Auth-profile secrets |
|
||||||
|
|
||||||
The three mounts follow upstream's own compose. `/home/node` as a whole is not
|
The three mounts are upstream's own. `/home/node` as a whole is not mounted:
|
||||||
mounted: the bundled Chromium lives in `/home/node/.cache`, and a volume there
|
the bundled Chromium lives in `/home/node/.cache`, and a volume there would
|
||||||
would freeze it at the first image's version.
|
freeze it at the first image's version. The image runs as the non-root `node`
|
||||||
|
user.
|
||||||
`cap_drop` and `no-new-privileges` are also upstream's; the image runs as the
|
|
||||||
non-root `node` user.
|
|
||||||
|
|
||||||
## Image
|
## Image
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,20 @@ services:
|
|||||||
openclaw:
|
openclaw:
|
||||||
build: .
|
build: .
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
init: true
|
||||||
|
command: ["node", "dist/index.js", "gateway", "--bind", "lan", "--port", "18789"]
|
||||||
environment:
|
environment:
|
||||||
- OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN:?required}
|
- OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_GATEWAY_TOKEN:?required}
|
||||||
- OPENCLAW_PUBLIC_ORIGIN=${OPENCLAW_PUBLIC_ORIGIN:?required}
|
- OPENCLAW_PUBLIC_ORIGIN=${OPENCLAW_PUBLIC_ORIGIN:?required}
|
||||||
- OPENCLAW_TRUSTED_PROXIES=${OPENCLAW_TRUSTED_PROXIES:-10.0.0.0/16}
|
- OPENCLAW_TRUSTED_PROXIES=${OPENCLAW_TRUSTED_PROXIES:-10.0.0.0/16}
|
||||||
|
- HOME=/home/node
|
||||||
|
- OPENCLAW_HOME=/home/node
|
||||||
|
- OPENCLAW_STATE_DIR=/home/node/.openclaw
|
||||||
|
- OPENCLAW_CONFIG_PATH=/home/node/.openclaw/openclaw.json
|
||||||
|
- OPENCLAW_CONFIG_DIR=/home/node/.openclaw
|
||||||
|
- OPENCLAW_WORKSPACE_DIR=/home/node/.openclaw/workspace
|
||||||
|
- OPENCLAW_GATEWAY_PORT=18789
|
||||||
|
- TERM=xterm-256color
|
||||||
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
|
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
|
||||||
- TZ=${TZ:-Asia/Ho_Chi_Minh}
|
- TZ=${TZ:-Asia/Ho_Chi_Minh}
|
||||||
# - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
# - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
||||||
@@ -44,6 +54,14 @@ services:
|
|||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
security_opt:
|
security_opt:
|
||||||
- no-new-privileges:true
|
- no-new-privileges:true
|
||||||
|
extra_hosts:
|
||||||
|
- host.docker.internal:host-gateway
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "node", "dist/docker-healthcheck.js"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 20s
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
openclaw-state:
|
openclaw-state:
|
||||||
|
|||||||
Reference in new issue
Block a user