mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-22 18:30:28 +00:00
221 lines
12 KiB
Markdown
221 lines
12 KiB
Markdown
# Hermes WebUI — Docker setup guide
|
|
|
|
This is the comprehensive Docker reference. For a 5-minute quickstart, see the [README Docker section](../README.md#docker).
|
|
|
|
## TL;DR — pick one
|
|
|
|
| Setup | When to use | File |
|
|
|---|---|---|
|
|
| **Single-container** (recommended) | You just want chat working. WebUI runs the agent in-process. | `docker-compose.yml` |
|
|
| **Two-container** | You want isolation between gateway (CLI/Telegram/cron) and chat UI. | `docker-compose.two-container.yml` |
|
|
| **Three-container** | Two-container PLUS the dashboard for monitoring. | `docker-compose.three-container.yml` |
|
|
| **All-in-one image** (community fork — third-party, not maintained by us) | Podman 3.4 / multi-arch / supervisord-style preference. | [sunnysktsang/hermes-suite](https://github.com/sunnysktsang/hermes-suite) — see [#1399](https://github.com/nesquena/hermes-webui/issues/1399) for the original discussion |
|
|
|
|
If something stops working, **start with the single-container setup** — it's the simplest path and fixes most permission/UID/path-mismatch issues by construction.
|
|
|
|
## Production image security model
|
|
|
|
The production Docker image is hardened for the normal single-tenant container threat model:
|
|
Hermes WebUI assumes one operator controls the container, mounted Hermes home, and workspace.
|
|
The image does **not** install `sudo`, does not add runtime users to a sudo group, and does not
|
|
grant `NOPASSWD` escalation. If an agent/tool process gains a shell as `hermeswebui`, it should
|
|
not be able to become root with a passwordless sudo command.
|
|
|
|
The entrypoint still starts as `root` for a narrow init phase because Docker bind mounts often need
|
|
UID/GID alignment and ownership preparation before the app can read `~/.hermes`, `/workspace`,
|
|
`/app`, and `/uv_cache`. After that setup, `docker_init.bash` re-execs itself as the unprivileged
|
|
`hermeswebui` user and starts the server there. Init scratch files under `/tmp/hermeswebui_init`
|
|
are owner-only (`0700` directory, `0600` files), not world-writable.
|
|
|
|
For multi-tenant or hostile-container environments, rebuild with your own runtime user, mount policy,
|
|
and supervisor assumptions. Development images that need package-manager convenience should add
|
|
those tools in a dev-only Dockerfile instead of reintroducing passwordless sudo to production.
|
|
|
|
## 5-minute quickstart (single container)
|
|
|
|
```bash
|
|
git clone https://github.com/nesquena/hermes-webui
|
|
cd hermes-webui
|
|
cp .env.docker.example .env
|
|
# Edit .env if needed (most users can skip this on Linux)
|
|
docker compose up -d
|
|
open http://localhost:8787
|
|
```
|
|
|
|
That's it. Your existing `~/.hermes` directory is mounted, your `~/workspace` is browsable, and the WebUI auto-detects your UID/GID from the mounted volume.
|
|
|
|
## What goes wrong (and how to fix it)
|
|
|
|
### 1. "Permission denied" at startup
|
|
|
|
**Symptom**: Container starts but immediately crashes, logs show:
|
|
```
|
|
PermissionError: [Errno 13] Permission denied: '/home/hermeswebui/.hermes/...'
|
|
```
|
|
|
|
**Cause**: The container's user (UID 1000 by default) can't read your bind-mounted directory because your host files are owned by a different UID.
|
|
|
|
**Fix**: Set `UID` and `GID` in `.env` to match your host:
|
|
```bash
|
|
echo "UID=$(id -u)" >> .env
|
|
echo "GID=$(id -g)" >> .env
|
|
docker compose down && docker compose up -d
|
|
```
|
|
|
|
On macOS, host UIDs start at 501. On Linux, the first interactive user is usually UID 1000.
|
|
|
|
> **macOS Docker Desktop**: if UID mapping still misbehaves after the env fix, try toggling **Settings → General → File sharing implementation** between VirtioFS and gRPC-FUSE. Different implementations preserve UIDs across the host/container boundary differently.
|
|
|
|
### 2. ".env file mode 0640 → permission denied" (#1389)
|
|
|
|
**Symptom**: You set `HERMES_HOME_MODE=0640` (or some other group-readable mode) on your host `.env` file, container starts, then errors out:
|
|
```
|
|
[security] fixed permissions on .env (0o640 -> 0600)
|
|
failed to load .env: open .env: permission denied
|
|
```
|
|
|
|
**Cause**: WebUI's `fix_credential_permissions()` startup hook enforces 0600 by default. This is the right thing for a clean install but conflicts with operator-set modes.
|
|
|
|
**Fix**: Set one of these env vars in your `.env`:
|
|
- `HERMES_SKIP_CHMOD=1` — bypass the fixer entirely
|
|
- `HERMES_HOME_MODE=0640` — allow group bits, only strip world-readable
|
|
|
|
Both are documented in `api/startup.py::fix_credential_permissions()`.
|
|
|
|
> ⚠️ **Multi-container warning**: `HERMES_HOME_MODE` has DIFFERENT semantics in the agent image vs. the WebUI:
|
|
> - **WebUI**: credential FILE mode threshold (`0640` allows group bits on `.env`)
|
|
> - **Agent**: `HERMES_HOME` *directory* mode (default `0700`)
|
|
>
|
|
> `0640` on a directory has no owner-execute bit, so the agent can't traverse its own home → bricked. For multi-container setups, use `HERMES_HOME_MODE=0750` (group-traversable) or `0701` (x-only). The compose files have per-service comments that match each side's semantics.
|
|
|
|
### 3. "Workspace appears empty even though my files are there"
|
|
|
|
**Symptom**: WebUI loads but `/workspace` shows no files.
|
|
|
|
**Cause**: Same as #1 — UID mismatch on the bind mount.
|
|
|
|
**Fix**: Same as #1 — match host UID/GID via `.env`.
|
|
|
|
### 4. "Two-container setup: WebUI can't find agent source" (#858)
|
|
|
|
**Symptom**: WebUI logs at startup:
|
|
```
|
|
!! WARNING: hermes-agent source not found.
|
|
!! Looked in: /home/hermeswebui/.hermes/hermes-agent
|
|
!! /opt/hermes
|
|
```
|
|
|
|
**Cause**: The agent's source (`/opt/hermes` inside the agent container) needs to be exposed to the WebUI container via a shared volume. The two-container compose file does this via `hermes-agent-src` named volume, but if you're using bind mounts incorrectly the path won't resolve.
|
|
|
|
**Fix**: Use the named volumes that ship with `docker-compose.two-container.yml` — don't replace them with bind mounts unless you know what you're doing. The agent container writes its source to `/opt/hermes`, and the WebUI mounts that volume at `/home/hermeswebui/.hermes/hermes-agent`.
|
|
|
|
If you must use a bind mount: pick a host path, then mount it to `/opt/hermes` in the agent container AND `/home/hermeswebui/.hermes/hermes-agent` in the WebUI container.
|
|
|
|
### 5. "Tools (git, node, etc.) missing in two-container setup" (#681)
|
|
|
|
**Symptom**: You ask the agent to run `git status` in chat and it errors with `command not found`.
|
|
|
|
**Cause**: This is **architectural, not a bug**. In the two-container setup, agent processes started by the WebUI run **inside the WebUI container**, not the agent container. The WebUI image doesn't include git/node by design (it's a UI image, not a tool host).
|
|
|
|
**Workarounds**:
|
|
- **Single-container setup** (`docker-compose.yml`) — everything in one container, no boundary
|
|
- **Custom WebUI image** — extend the `Dockerfile` to install the tools you need
|
|
- **Combined image** ([sunnysktsang/hermes-suite](https://github.com/sunnysktsang/hermes-suite)) — community fork that ships agent+webui+dashboard in one container
|
|
|
|
### 6. "config.yaml not loaded"
|
|
|
|
**Symptom**: You have a `config.yaml` in your host `~/.hermes/`, but the WebUI shows "no model configured" or doesn't pick up your custom providers.
|
|
|
|
**Cause**: Either the file isn't readable (UID/GID issue, see #1) or it's not in the expected path inside the container.
|
|
|
|
**Fix**:
|
|
- Verify: `docker exec hermes-webui ls -la /home/hermeswebui/.hermes/config.yaml`
|
|
- If it doesn't exist: your host bind mount is pointing at the wrong directory.
|
|
- If it exists but is unreadable: see #1 for the UID/GID fix.
|
|
|
|
### 7. "On Podman: can't share .hermes between containers"
|
|
|
|
**Symptom**: Two-container setup works on Docker but fails on Podman with permission errors no matter what UID/GID you set.
|
|
|
|
**Cause**: Podman 3.4 (Ubuntu 22.04 default) has limited support for `userns_mode: keep-id` across multiple containers — files written by one container appear with a different UID in the other.
|
|
|
|
**Fix**: Either upgrade to Podman 4+ (which fixes this), or use the [single-container setup](#5-minute-quickstart-single-container), or use the [community all-in-one image](https://github.com/sunnysktsang/hermes-suite).
|
|
|
|
## Multi-container architecture
|
|
|
|
The two- and three-container setups use **named Docker volumes** (not bind mounts) by default for a reason: named volumes solve the UID/GID problem by construction. Docker creates the volume's root directory with the correct ownership, all containers reading/writing to it see the same files, no host-side permission setup required.
|
|
|
|
```
|
|
┌─────────────────────────────────┐
|
|
│ hermes-home (volume) │
|
|
│ (config, sessions, state, ...) │
|
|
└─────────────────────────────────┘
|
|
↑ ↑
|
|
│ rw │ rw
|
|
│ │
|
|
┌──────────────┐ │ │ ┌──────────────┐
|
|
│ hermes-agent │────┘ └────│ hermes-webui │
|
|
│ (port 8642) │ │ (port 8787) │
|
|
└──────────────┘ └──────────────┘
|
|
│ ↑
|
|
│ rw │ ro
|
|
↓ │
|
|
┌─────────────────────────┐ │
|
|
│ hermes-agent-src (vol) │─────────────────────┘
|
|
│ (agent's Python source) │
|
|
└─────────────────────────┘
|
|
```
|
|
|
|
The WebUI container doesn't ship with the agent's Python deps — at startup it runs `uv pip install /home/hermeswebui/.hermes/hermes-agent` to install them from the shared volume.
|
|
|
|
## Bind-mount migration (advanced)
|
|
|
|
If you really need to bind-mount an existing host `~/.hermes` (e.g. you're keeping config in dotfiles, sharing with a non-Docker `hermes` install, etc.):
|
|
|
|
```yaml
|
|
volumes:
|
|
hermes-home:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: /home/youruser/.hermes
|
|
hermes-agent-src:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: /opt/hermes-agent-source
|
|
```
|
|
|
|
**Critical requirements**:
|
|
|
|
1. The host directory MUST be readable by your container UID. Run `id -u` on the host and ensure `~/.hermes` is owned by that UID (or readable via group bits).
|
|
2. ALL containers sharing the volume must run as the SAME UID/GID. Set `UID=$(id -u)` and `GID=$(id -g)` in `.env`.
|
|
3. If your host `.env` is mode 0640, set `HERMES_SKIP_CHMOD=1` or `HERMES_HOME_MODE=0640` so the startup hook doesn't try to enforce 0600.
|
|
|
|
## Reference
|
|
|
|
- [`docker-compose.yml`](../docker-compose.yml) — single container (recommended)
|
|
- [`docker-compose.two-container.yml`](../docker-compose.two-container.yml) — agent + webui
|
|
- [`docker-compose.three-container.yml`](../docker-compose.three-container.yml) — agent + dashboard + webui
|
|
- [`.env.docker.example`](../.env.docker.example) — environment variable template
|
|
- [`Dockerfile`](../Dockerfile) — single-container build
|
|
- [`docker_init.bash`](../docker_init.bash) — container entrypoint script
|
|
|
|
## Related issues
|
|
|
|
- #1389 — `HERMES_HOME_MODE` override (fixed in v0.50.254 — agent honors `HERMES_SKIP_CHMOD` and `HERMES_HOME_MODE`)
|
|
- #1399 — UID alignment in compose files (fixed in v0.50.260 via PR #1428 + this guide)
|
|
- #858 — two-container `/opt/hermes` path confusion
|
|
- #681 — tools running in WebUI container, not agent container (architectural)
|
|
- #668 — auto-detect UID/GID from mounted volume
|
|
- #569 — UID/GID detection priority order
|
|
|
|
If you hit a new failure mode not covered here, please [open an issue](https://github.com/nesquena/hermes-webui/issues/new) with:
|
|
|
|
1. Which compose file you used
|
|
2. The error from `docker logs hermes-webui`
|
|
3. `docker exec hermes-webui id` output
|
|
4. `docker exec hermes-webui ls -la /home/hermeswebui/.hermes` output
|