-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathdocker-compose.three-container.yml
More file actions
168 lines (163 loc) · 7.23 KB
/
docker-compose.three-container.yml
File metadata and controls
168 lines (163 loc) · 7.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# Three-container Docker Compose: Hermes Agent + Dashboard + WebUI
#
# QUICK START:
# docker compose -f docker-compose.three-container.yml up -d
# Open http://localhost:8787 (chat) and http://localhost:9119 (dashboard)
#
# This extends the two-container setup with the Hermes Dashboard for
# monitoring agent activity, sessions, and resource usage.
#
# Services:
# hermes-agent — gateway API on port 8642 (CLI, Telegram, cron, tools)
# hermes-dashboard — monitoring dashboard on port 9119
# hermes-webui — browser chat interface on port 8787
#
# All three share the same hermes-home volume so config, sessions,
# skills, and memory are consistent across all surfaces.
#
# WHEN NOT TO USE THIS:
# - You hit "Permission denied" trying to share an existing ~/.hermes directory
# → use docker-compose.yml (single-container) instead, OR
# → keep this file but switch to NAMED VOLUMES (the default) instead of bind mounts
# - You're on Podman 3.4 or older without keep-id namespace support
# → see https://github.com/sunnysktsang/hermes-suite for an all-in-one image
#
# KNOWN LIMITATION (#681): tools triggered from the WebUI run in the WebUI
# container, not the agent container. See docker-compose.two-container.yml
# for context.
#
# NOTE ON VOLUMES:
# This file uses named Docker volumes (hermes-home, hermes-agent-src) which
# work out of the box. If you prefer bind mounts (e.g. to an existing
# directory), see docker-compose.two-container.yml for a bind-mount example.
# When using bind mounts, ALL THREE containers must mount the same host path
# AND run as the same UID/GID (set via the UID/GID env vars below).
services:
hermes-agent:
image: nousresearch/hermes-agent:latest
container_name: hermes-agent
command: gateway run
ports:
- "127.0.0.1:8642:8642"
volumes:
# Persist config, state, sessions, skills, memory across restarts
- hermes-home:/home/hermes/.hermes
# Expose agent source so the WebUI can install dependencies from it
- hermes-agent-src:/opt/hermes
environment:
- HERMES_HOME=/home/hermes/.hermes
# Align UID/GID across containers sharing the hermes-home volume.
# Defaults to 1000 to match WANTED_UID/WANTED_GID in the webui service.
- HERMES_UID=${UID:-1000}
- HERMES_GID=${GID:-1000}
# Bind-mount permission handling for the agent — narrow set of overrides.
# NOTE: The agent's HERMES_HOME_MODE applies to the HERMES_HOME *directory*
# mode (default 0700) — NOT to credential files like the WebUI's variant.
# If you set this, you MUST keep the owner-execute bit so the agent can
# traverse its own home directory. 0640 BREAKS the agent (no x bit → no
# traversal). Use 0750 for group-traversable or 0701 for x-only.
# The agent's container detection (/.dockerenv) already auto-skips
# credential chmod inside Docker, so HERMES_SKIP_CHMOD is redundant here.
# - HERMES_HOME_MODE=0750
restart: unless-stopped
deploy:
resources:
limits:
memory: 4G
cpus: "2.0"
networks:
- hermes-net
hermes-dashboard:
image: nousresearch/hermes-agent:latest
container_name: hermes-dashboard
command: dashboard --host 0.0.0.0 --insecure
ports:
- "127.0.0.1:9119:9119"
volumes:
- hermes-home:/home/hermes/.hermes
environment:
- HERMES_HOME=/home/hermes/.hermes
# Align UID/GID across containers sharing the hermes-home volume.
# Defaults to 1000 to match WANTED_UID/WANTED_GID in the webui service.
- HERMES_UID=${UID:-1000}
- HERMES_GID=${GID:-1000}
# Dashboard connects to the gateway for health/session data
- GATEWAY_HEALTH_URL=http://hermes-agent:8642
depends_on:
- hermes-agent
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
cpus: "0.5"
networks:
- hermes-net
hermes-webui:
image: ghcr.io/nesquena/hermes-webui:latest
container_name: hermes-webui
depends_on:
- hermes-agent
ports:
# Expose on localhost only. Remove 127.0.0.1: to expose on all interfaces
# (set HERMES_WEBUI_PASSWORD if doing so).
- "127.0.0.1:8787:8787"
volumes:
# Same hermes home as the agent — shares config, sessions, state
- hermes-home:/home/hermeswebui/.hermes
# Agent source mounted where docker_init.bash expects it.
# Mounted read-only — the WebUI only reads this volume to install
# the agent's Python dependencies at startup (`uv pip install`).
# Read-only enforces that defence-in-depth at the kernel layer.
- hermes-agent-src:/home/hermeswebui/.hermes/hermes-agent:ro
# Workspace directory — browse and edit files from the WebUI.
# Adapt the host path to your project directory.
# ${HOME} is used rather than `~` so the default resolves the same way
# across Linux, macOS, WSL2, and Docker Desktop on Windows.
- ${HERMES_WORKSPACE:-${HOME}/workspace}:/workspace
environment:
- HERMES_WEBUI_HOST=0.0.0.0
- HERMES_WEBUI_PORT=8787
- HERMES_WEBUI_STATE_DIR=/home/hermeswebui/.hermes/webui
# Match your host user's UID/GID for correct file permissions.
# Run `id -u` and `id -g` to find your values.
# On macOS, UIDs start at 501 (not 1000) — set these in a .env file:
# echo "UID=$(id -u)" >> .env && echo "GID=$(id -g)" >> .env
- WANTED_UID=${UID:-1000}
- WANTED_GID=${GID:-1000}
# NOTE: When using bind-mount volumes shared across containers, ALL containers
# that write to the same host directory must run as the same UID/GID.
# If hermes-agent initialises the state dir as root (UID 0), hermes-webui
# will get a PermissionError accessing those paths — including a crash on every
# HTTP request if the auth signing-key file is unreadable. Either set WANTED_UID
# to match the agent container's UID, or use a named Docker volume (preferred).
# Optional: set a password for remote access
# - HERMES_WEBUI_PASSWORD=your-secret-password
# Bind-mount permission handling for the WebUI (fixes #1389, #1399).
# NOTE: WebUI's HERMES_HOME_MODE is a credential-file threshold (allow
# group bits on .env/.signing_key/etc.), DIFFERENT from the agent's
# which applies to the HERMES_HOME directory itself. 0640 is correct
# for the WebUI; do NOT copy this value to the agent service block.
# - HERMES_SKIP_CHMOD=1
# - HERMES_HOME_MODE=0640
restart: unless-stopped
networks:
- hermes-net
networks:
hermes-net:
driver: bridge
volumes:
# IMPORTANT — upgrading the agent image:
# The `hermes-agent-src` volume is initialised from the agent image's
# `/opt/hermes` on first `up`, and Docker reuses the volume verbatim on
# later runs — even after `docker pull` of a newer agent image. After
# upgrading the agent image, run:
#
# docker compose -f docker-compose.three-container.yml down
# docker volume rm <project>_hermes-agent-src
# docker compose -f docker-compose.three-container.yml pull
# docker compose -f docker-compose.three-container.yml up -d
#
# The full procedure (and why) is documented in docs/docker.md.
hermes-home:
hermes-agent-src: