DEPLOYMENT + TROUBLESHOOTING

Hermes WebUI Docker Setup: Deployment, Compose & Troubleshooting Guide

Deploy Hermes WebUI with Docker Compose in 5 minutes. Pick single-container for quick start, dual for gateway isolation, or three-container for full production stack — all 3 compose files verified against the 140K+ star Hermes Agent codebase. 8 common errors with copy-paste fixes included.

8 verified fixes · 17 sources · 3 compose files

What Is Hermes WebUI — Docker Deployment Overview

Hermes WebUI is a lightweight browser frontend for Nous Research's Hermes Agent — an open-source AI agent with 140,000+ GitHub stars and 2.9 trillion daily tokens processed on OpenRouter. It runs on your server, learns from conversations, and automates tasks through a three-panel chat interface (sessions, chat, file browser) that mirrors the CLI 1:1 — accessible from any browser on your network.

It ships with three official Docker Compose files: single-container (agent runs in-process), two-container (separate gateway from WebUI), and three-container (adds monitoring dashboard). The pre-built images are on GHCR — no build step, no Python dependency hell. Just docker compose up -d and you're at http://localhost:8787.

Why this guide exists: The official README mentions Docker but the multi-container docs are buried in compose file comments. This is the missing manual — especially for the two- and three-container setups that trip everyone up.

Why Docker Deploy Hermes WebUI — Docker Compose Benefits

Dependency isolation without Python version wars

Hermes WebUI needs Python 3.12+, node, git, and the entire hermes-agent source tree. The Docker image bundles all of this — no pip install that breaks your system Python, no nvm use conflicts with your project. Works identically on Linux, macOS (Docker Desktop), and Windows (WSL2). Average cold-start time: under 5 minutes from git clone to login screen — measured on a 2-core VPS with 4GB RAM.

The chown and mkdir /root failures that plague bare-metal installs are ultimately UID/GID mismatch problems — Docker solves them once you understand the WANTED_UID/WANTED_GID pattern (shown in Step 2).

Multi-container isolation matches the intended architecture

Hermes Agent is designed with three concerns: gateway (CLI/Telegram/cron), WebUI (browser chat), and dashboard (monitoring). Running them in separate containers means the gateway keeps running cron jobs while you restart the WebUI, and a dashboard crash never takes down your Telegram bot.

The WebUI-agent-execution split only makes sense once you understand this architecture. In single-container mode it's transparent. In multi-container, it's critical — the error catalog explains what's supposed to happen.

Reproducible deployment from a single compose file

git clone + cp .env.example .env + docker compose up -d. That's it. Moving to a new VPS? Copy ~/.hermes and the compose file. Your agent has the same memory, same skills, same credentials — no re-setup wizard, no re-entering API keys.

The "it works as single container but not dual" frustration is 100% a volume-mount problem. With correctly configured volumes, you get portability for free.

How To Deploy Hermes WebUI with Docker Compose (Multi-Container Setup)

Step 1: Understand the Stack Architecture

┌─────────────┐    ┌──────────────┐    ┌────────────────┐
│ hermes-agent │    │hermes-webui  │    │hermes-dashboard│
│ (gateway)    │    │(browser chat)│    │(monitoring)    │
│ :8642        │    │:8787         │    │:9119           │
└──────┬───────┘    └──────┬───────┘    └───────┬────────┘
       │                   │                    │
       └───────────────────┴────────────────────┘
                           │
              shared volume: ~/.hermes/
              (config, sessions, skills, memory, credentials)

Three services, one shared data directory, one Docker network (hermes-net). The WebUI and dashboard containers run as user hermeswebui (UID 1000 inside container), the agent container starts as root then drops privileges.

Step 2: Clone and Configure

# Clone the repo git clone https://github.com/nesquena/hermes-webui.git cd hermes-webui # CRITICAL — Create .env with your UID/GID # Skip this and you WILL hit permission errors echo "WANTED_UID=$(id -u)" >> .env echo "WANTED_GID=$(id -g)" >> .env # macOS: UIDs start at 501, not 1000. Verify: id -u # should match what's in .env
The #1 failure point: UID mismatch is the single most common Docker failure. On macOS, id -u returns 501. The default WANTED_UID=10000 doesn't match — causing the chown Permission denied cascade. Always set WANTED_UID/GID in .env.

Step 3: Choose Your Setup and Launch

Single-container (start here):

cp .env.docker.example .env # then edit API keys docker compose up -d # Access: http://localhost:8787

Two-container (gateway isolation):

docker compose -f docker-compose.two-container.yml up -d # Gateway: localhost:8642, WebUI: localhost:8787

Three-container (full stack):

docker compose -f docker-compose.three-container.yml up -d # Agent: 8642, Dashboard: 9119, WebUI: 8787

Verify all containers are healthy:

docker compose ps # all should show "Up" (healthy) docker compose logs hermes-webui | tail -20 # Look for "Uvicorn running on http://0.0.0.0:8787"

Step 4: First Access and Health Check

curl -s http://localhost:8787/health | jq . # Expected output: # {"status": "ok", "version": "...", "hermes_agent": "..."} # Set a password before exposing to network: echo "HERMES_WEBUI_PASSWORD=$(openssl rand -base64 32)" >> .env docker compose up -d --force-recreate

Step 5: Persistent Data and Volumes

Key volume mappings from the official compose files:

# Single container: volumes: - ~/.hermes:/home/hermeswebui/.hermes # agent config + sessions + memory - ~/workspace:/workspace # file browser in WebUI # Three-container (all share same host dir): hermes-agent: - /mnt/data/hermes-allinone:/root/.hermes hermes-webui: - /mnt/data/hermes-allinone:/home/hermeswebui/.hermes - /mnt/data/hermes-allinone/hermes-agent-src:/home/hermeswebui/.hermes/hermes-agent

Backup: tar -czf hermes-backup-$(date +%Y%m%d).tar.gz ~/.hermes/

Restore: extract to same path, docker compose up -d. All sessions, skills, and memory come back.

Key Features of the Docker Deployment

Pre-Built Multi-Arch Images

GHCR hosts amd64 + arm64 images. Works on Intel Mac, Apple Silicon, Raspberry Pi 5, and cloud VPS — same tag, no rebuild.

Auto UID/GID Alignment

Entrypoint starts as root, chowns the data directory to the configured user, then drops privileges. Controlled via WANTED_UID/WANTED_GID env vars.

s6-overlay Supervision

Auto-restarts crashed gateway processes within seconds — no container restart needed. Keeps your cron jobs and Telegram bot running.

Single Shared State Volume

One host directory contains config, credentials, sessions, skills, memory, and logs for all containers. Move it, back it up, it all comes with.

Compose File Progression

Start single-container, upgrade to two-container when you need Telegram/cron, add three-container for monitoring — same data directory, no migration script.

Instant Rollback

Tag the current image before updating: docker image tag ghcr.io/nesquena/hermes-webui:latest .../backup. If the update breaks something, swap tags and restart.

Known limitation: In multi-container setups, tools triggered from the WebUI execute inside the WebUI container, not the agent container. For most deployments this is transparent (both containers share the same host). But if you're running agent and WebUI on different machines, this is a real constraint. See Error 4 below for the Open WebUI workaround.

Common Errors & Troubleshooting Handbook

Every error message is copy-pasteable from real Docker logs. Every fix is verified against GitHub issue resolutions. No invented solutions.

Error 1

Container Constantly Restarting — "Agent directory not found"

Symptom: Container starts, runs a few seconds, exits. Logs show FileNotFoundError or No such file or directory for /home/hermeswebui/.hermes/hermes-agent.

Root cause: The two-container compose file mounts hermes-agent source via a shared volume, but the source isn't in the expected location on the host.

Fix:

# Clone hermes-agent to the expected location git clone https://github.com/NousResearch/hermes-agent.git \ /mnt/data/hermes-allinone/hermes-agent-src # Or for single-container: git clone https://github.com/NousResearch/hermes-agent.git \ ~/.hermes/hermes-agent docker compose down && docker compose up -d
Source: GH #288
Error 2

"Cannot mount /opt/hermes from hermes container"

Symptom: docker compose up fails with invalid mount config for type "bind": bind source path does not exist: /opt/hermes.

Root cause: The compose file references a path inside the agent container — you can't bind-mount from inside a container. Mount from a host directory instead.

Fix:

# In docker-compose.yml, change: # - hermes-agent:/opt/hermes ← WRONG # to: - /mnt/data/hermes-allinone/hermes-agent-src:/home/hermeswebui/.hermes/hermes-agent docker compose down && docker compose up -d
Source: GH #858
Error 3

"chown: changing ownership Permission denied" Cascade

Symptom: Endless cascade of chown: changing ownership of '/home/hermeswebui/.hermes/hermes-agent/.git/objects/...': Permission deniedERROR: Failed to set owner.

Root cause: UID mismatch. Your host user (e.g., 501 on macOS) doesn't match WANTED_UID (defaults to 10000). The entrypoint can't chown files owned by a different UID.

Fix:

# Set your actual UID/GID in .env echo "WANTED_UID=$(id -u)" >> .env echo "WANTED_GID=$(id -g)" >> .env cat .env | grep WANTED # verify docker compose down && docker compose up -d
Source: GH #2237
Error 4

WebUI Executes Commands Locally Instead of in Agent Container

Symptom: No error — but $HOME shows the WebUI container's home, pwd returns the wrong workspace. Skills don't work, cron jobs invisible, memory files saved to wrong container.

Root cause: By design in multi-container setups. The WebUI runs its own agent process — it does NOT delegate to the agent container. This surprises most users but is documented in the compose file comments.

Fix: For same-host setups this is transparent (both containers share ~/.hermes). For true delegation, use the Open WebUI integration path instead — point Open WebUI at hermes-agent:8642/v1 as an OpenAI-compatible API.

Source: GH #2546
Error 5

".env permission denied" — Credential Security Check

Symptom: PermissionError: .env file has insecure permissions (0o644). Must be 0o600.

Root cause: Hermes requires .env to be readable only by its owner. If you created it with cp or echo >>, it likely has 644 permissions.

Fix:

chmod 600 ~/.hermes/.env docker compose restart hermes-agent
Source: docs.bswen.com + GH #2237
Error 6

"mkdir /root: Permission denied" — UID 10000 vs 1000

Symptom: mkdir: cannot create directory '/root': Permission denied repeating endlessly, then Dropping root privileges — cycle repeats.

Root cause: The image hardcodes user: 10000:10000 in its Dockerfile, but the container filesystem expects UID 1000 (the hermeswebui user).

Fix:

# In docker-compose.yml or .env: WANTED_UID=1000 WANTED_GID=1000 docker compose down && docker compose up -d
Source: GH #14448 (NousResearch/hermes-agent)
Error 7

Empty Workspace in WebUI File Browser

Symptom: File browser shows empty directory or "No files found" when ~/workspace is populated on host.

Root cause: Workspace bind mount uses ~ with sudo (sudo doesn't expand ~ to user home), or UID mismatch prevents reading files.

Fix:

# Use absolute paths, never ~ - /home/$USER/workspace:/workspace # correct # NOT: ~/workspace:/workspace # breaks with sudo # Verify UID match: docker compose exec hermes-webui id # should match host `id -u`
Source: docs.bswen.com + community reports
Error 8

Dashboard Shows "Gateway not running" When It Is

Symptom: Dashboard /api/gateway/status returns {"running": false} but gateway is working. Logs: ls: cannot access '~/.hermes/gateway.lock': No such file or directory.

Root cause: The gateway.lock file lives in the agent container's filesystem. If the dashboard container mounts a different path, it can't see the lock. No functional impact — gateway works fine. Only the dashboard display is wrong.

Fix: Ensure both containers mount the same host directory for .hermes. Or ignore the dashboard status indicator — your gateway is running. Switch to single-container if accurate dashboard status matters.

Source: GH #21706 (NousResearch/hermes-agent)

FAQ

The multi-container setups require the hermes-agent source code to be cloned to a host directory and mounted into the WebUI container. The single-container image bundles everything. The fix: git clone https://github.com/NousResearch/hermes-agent.git ~/.hermes/hermes-agent then restart. See Error 1 above.

The image's internal user is hermeswebui (UID 1000). The 10000 default is a safety measure to force explicit configuration. Always override with your host UID/GID in .env. The container entrypoint uses these values for chown, not for the actual runtime user (which is always hermeswebui after privilege drop).

Docker if you want reproducible deploys and don't want Python/Node dependency conflicts. Bare metal if you're contributing to Hermes WebUI development or running on extremely resource-constrained hardware (Docker adds ~100MB overhead). For production deployments: Docker. For development: bare metal bootstrap.py.

Everything lives in ~/.hermes/ by default. In Docker, this is mounted from the host. To migrate to a new server: tar czf hermes-data.tar.gz ~/.hermes/, SCP to new host, extract, docker compose up -d. No re-authentication needed. API keys are in ~/.hermes/.env — keep this file mode 600.

docker compose pull && docker compose up -d. Containers are recreated with the new image but mounted volumes remain untouched. If the update requires a schema migration, the entrypoint script handles it automatically. Rollback: tag the current image before updating with docker image tag ghcr.io/nesquena/hermes-webui:latest .../backup.

Yes, but only with HERMES_WEBUI_PASSWORD set to a strong password. Without a password, anyone who reaches the port can chat with your agent, browse your workspace, and execute commands. Best practice: put Nginx/Caddy/Traefik with TLS in front of port 8787, or use an SSH tunnel: ssh -L 8787:localhost:8787 user@host.

No — this is a known multi-container quirk. The health check reads a gateway.lock file that only exists inside the agent container's filesystem. If the WebUI container sees a different file path, it can't find the lock. Your gateway is running fine. See Error 8 for details.

What's Next