Covers ~60 merged PRs from Apr 15–29 that shipped user-visible behavior without docs coverage. No functional code changes; docs + static manifest regeneration only. Highlights: Stale / incorrect: - configuration.md: auxiliary auto-routing line was wrong since #11900; now correctly states auto routes to the main model, with a note on the cost trade-off and per-task override pattern. - integrations/providers.md + configuration.md compression intro: removed stale 'Gemini Flash via OpenRouter' claim. - website/static/api/model-catalog.json: rebuilt from hermes_cli/models.py so the live manifest picks up tencent/hy3-preview (and remains in sync for future model-catalog PRs). Platform messaging (#17417 #16997 #16193 #14315 #13151 #11794 #10610 #10283 #10246 #11564 #13178): - Signal: native formatting (bodyRanges), reply quotes, reactions. - Telegram: table rendering (bullets + code-block fallback), disable_link_previews, group_allowed_chats. - Slack: strict_mention config. - Discord: slash_commands disable, send_animation GIF, send_message native media attachments. - DingTalk: require_mention + allowed_users. CLI (#16052 #16539 #16566 #15841 #14798 #10043): - New 'hermes fallback' interactive manager. - New 'hermes update --check', '--backup' flag, and pre-update pairing snapshot behavior. - 'hermes gateway start/restart --all' multi-profile flag. - cron.md: 'hermes tools' as a platform, per-job enabled_toolsets, wakeAgent gate, context_from chaining. Config keys / env vars (#17305 #17026 #17000 #15077 #14557 #14227 #14166 #14730 #17008): - terminal.docker_run_as_host_user, display.runtime_metadata_footer, compression.hygiene_hard_message_limit, HINDSIGHT_TIMEOUT, skills.guard_agent_created, TAVILY_BASE_URL, security.allow_private_urls, agent.api_max_retries, gateway hot-reload of compression/context_length config edits. TUI / CLI UX (#17130 #17113 #17175 #17150 #16707 #12312 #12305 #12934 #14810 #14045 #17286 #17126): - HERMES_TUI_RESUME, HERMES_TUI_THEME, LaTeX rendering, busy-indicator styles, ctrl-x queued-message delete, git branch in status bar, per- prompt elapsed stopwatch, external-editor keybind, markdown stripping, TUI voice-mode parity, /agents overlay, /reload + /mouse. Gateway features (#16506 #15027 #13428 #12116): - Native multimodal image routing based on vision capability. - /usage account-limits section. - /steer slash command (added to reference + explanation in CLI). Plugins / hooks (#12929 #12972 #10763 #16364): - transform_tool_result, transform_terminal_output plugin hooks. - PluginContext.dispatch_tool() documented with slash-command example. - google_meet bundled plugin entry under built-in-plugins.md. Other (#16576 #16572 #16383 #15878 #15608 #15606 #14809 #14767 #14231 #14232 #14307 #13683 #12373 #11891 #11291 #10066): - hermes backup exclusions (WAL/SHM/journal + checkpoints/). - security.md hardline blocklist (floor below --yolo). - FHS install layout for root installs. - openssh-client + docker-cli baked into the Docker image. - MEDIA: tag supported extensions table (docs/office/archives/pdf). - Remote-to-host file sync on SSH/Modal/Daytona teardown. - 'hermes model' -> Configure Auxiliary Models interactive picker. - Podman support via HERMES_DOCKER_BINARY. Providers / STT / one-shot (#15045 #14473 #15704): - alibaba-coding-plan first-class provider entry. - xAI Grok STT as a 6th transcription option. - 'hermes -z' scripted one-shot mode + HERMES_INFERENCE_MODEL. Build: 'docusaurus build' succeeds. No new broken links/anchors; pre-existing warnings unchanged.
12 KiB
sidebar_position, title, description
| sidebar_position | title | description |
|---|---|---|
| 7 | Docker | Running Hermes Agent in Docker and using Docker as a terminal backend |
Hermes Agent — Docker
There are two distinct ways Docker intersects with Hermes Agent:
- Running Hermes IN Docker — the agent itself runs inside a container (this page's primary focus)
- Docker as a terminal backend — the agent runs on your host but executes commands inside a Docker sandbox (see Configuration → terminal.backend)
This page covers option 1. The container stores all user data (config, API keys, sessions, skills, memories) in a single directory mounted from the host at /opt/data. The image itself is stateless and can be upgraded by pulling a new version without losing any configuration.
Quick start
If this is your first time running Hermes Agent, create a data directory on the host and start the container interactively to run the setup wizard:
mkdir -p ~/.hermes
docker run -it --rm \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent setup
This drops you into the setup wizard, which will prompt you for your API keys and write them to ~/.hermes/.env. You only need to do this once. It is highly recommended to set up a chat system for the gateway to work with at this point.
Running in gateway mode
Once configured, run the container in the background as a persistent gateway (Telegram, Discord, Slack, WhatsApp, etc.):
docker run -d \
--name hermes \
--restart unless-stopped \
-v ~/.hermes:/opt/data \
-p 8642:8642 \
nousresearch/hermes-agent gateway run
Port 8642 exposes the gateway's OpenAI-compatible API server and health endpoint. It's optional if you only use chat platforms (Telegram, Discord, etc.), but required if you want the dashboard or external tools to reach the gateway.
Opening any port on an internet facing machine is a security risk. You should not do it unless you understand the risks.
Running the dashboard
The built-in web dashboard can run alongside the gateway as a separate container.
To run the dashboard as its own container, point it at the gateway's health endpoint so it can detect gateway status across containers:
docker run -d \
--name hermes-dashboard \
--restart unless-stopped \
-v ~/.hermes:/opt/data \
-p 9119:9119 \
-e GATEWAY_HEALTH_URL=http://$HOST_IP:8642 \
nousresearch/hermes-agent dashboard
Replace $HOST_IP with the IP address of the machine running the gateway container (e.g. 192.168.1.100), or use a Docker network hostname if both containers share a network (see the Compose example below).
| Environment variable | Description | Default |
|---|---|---|
GATEWAY_HEALTH_URL |
Base URL of the gateway's API server, e.g. http://gateway:8642 |
(unset — local PID check only) |
GATEWAY_HEALTH_TIMEOUT |
Health probe timeout in seconds | 3 |
Without GATEWAY_HEALTH_URL, the dashboard falls back to local process detection — which only works when the gateway runs in the same container or on the same host.
Running interactively (CLI chat)
To open an interactive chat session against a running data directory:
docker run -it --rm \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent
Or if you have already opened a terminal in your running container (via Docker Desktop for instance), just run:
/opt/hermes/.venv/bin/hermes
Persistent volumes
The /opt/data volume is the single source of truth for all Hermes state. It maps to your host's ~/.hermes/ directory and contains:
| Path | Contents |
|---|---|
.env |
API keys and secrets |
config.yaml |
All Hermes configuration |
SOUL.md |
Agent personality/identity |
sessions/ |
Conversation history |
memories/ |
Persistent memory store |
skills/ |
Installed skills |
cron/ |
Scheduled job definitions |
hooks/ |
Event hooks |
logs/ |
Runtime logs |
skins/ |
Custom CLI skins |
:::warning Never run two Hermes gateway containers against the same data directory simultaneously — session files and memory stores are not designed for concurrent write access. Running a dashboard container alongside the gateway is safe since the dashboard only reads data. :::
Multi-profile support
Hermes supports multiple profiles — separate ~/.hermes/ directories that let you run independent agents (different SOUL, skills, memory, sessions, credentials) from a single installation. When running under Docker, using Hermes' built-in multi-profile feature is not recommended.
Instead, the recommended pattern is one container per profile, with each container bind-mounting its own host directory as /opt/data:
# Work profile
docker run -d \
--name hermes-work \
--restart unless-stopped \
-v ~/.hermes-work:/opt/data \
-p 8642:8642 \
nousresearch/hermes-agent gateway run
# Personal profile
docker run -d \
--name hermes-personal \
--restart unless-stopped \
-v ~/.hermes-personal:/opt/data \
-p 8643:8642 \
nousresearch/hermes-agent gateway run
Why separate containers over profiles in Docker:
- Isolation — each container has its own filesystem, process table, and resource limits. A crash, dependency change, or runaway session in one profile can't affect another.
- Independent lifecycle — upgrade, restart, pause, or roll back each agent separately (
docker restart hermes-workleaveshermes-personaluntouched). - Clean port and network separation — each gateway binds its own host port; there's no risk of cross-talk between chat platforms or API servers.
- Simpler mental model — the container is the profile. Backups, migrations, and permissions all follow the bind-mounted directory, with no extra
--profileflags to remember. - Avoids concurrent-write risk — the warning above about never running two gateways against the same data directory still applies to profiles within a single container.
In Docker Compose, this just means declaring one service per profile with distinct container_name, volumes, and ports:
services:
hermes-work:
image: nousresearch/hermes-agent:latest
container_name: hermes-work
restart: unless-stopped
command: gateway run
ports:
- "8642:8642"
volumes:
- ~/.hermes-work:/opt/data
hermes-personal:
image: nousresearch/hermes-agent:latest
container_name: hermes-personal
restart: unless-stopped
command: gateway run
ports:
- "8643:8642"
volumes:
- ~/.hermes-personal:/opt/data
Environment variable forwarding
API keys are read from /opt/data/.env inside the container. You can also pass environment variables directly:
docker run -it --rm \
-v ~/.hermes:/opt/data \
-e ANTHROPIC_API_KEY="sk-ant-..." \
-e OPENAI_API_KEY="sk-..." \
nousresearch/hermes-agent
Direct -e flags override values from .env. This is useful for CI/CD or secrets-manager integrations where you don't want keys on disk.
Docker Compose example
For persistent deployment with both the gateway and dashboard, a docker-compose.yaml is convenient:
services:
hermes:
image: nousresearch/hermes-agent:latest
container_name: hermes
restart: unless-stopped
command: gateway run
ports:
- "8642:8642"
volumes:
- ~/.hermes:/opt/data
networks:
- hermes-net
# Uncomment to forward specific env vars instead of using .env file:
# environment:
# - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
# - OPENAI_API_KEY=${OPENAI_API_KEY}
# - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
deploy:
resources:
limits:
memory: 4G
cpus: "2.0"
dashboard:
image: nousresearch/hermes-agent:latest
container_name: hermes-dashboard
restart: unless-stopped
command: dashboard --host 0.0.0.0
ports:
- "9119:9119"
volumes:
- ~/.hermes:/opt/data
environment:
- GATEWAY_HEALTH_URL=http://hermes:8642
networks:
- hermes-net
depends_on:
- hermes
deploy:
resources:
limits:
memory: 512M
cpus: "0.5"
networks:
hermes-net:
driver: bridge
Start with docker compose up -d and view logs with docker compose logs -f.
Resource limits
The Hermes container needs moderate resources. Recommended minimums:
| Resource | Minimum | Recommended |
|---|---|---|
| Memory | 1 GB | 2–4 GB |
| CPU | 1 core | 2 cores |
| Disk (data volume) | 500 MB | 2+ GB (grows with sessions/skills) |
Browser automation (Playwright/Chromium) is the most memory-hungry feature. If you don't need browser tools, 1 GB is sufficient. With browser tools active, allocate at least 2 GB.
Set limits in Docker:
docker run -d \
--name hermes \
--restart unless-stopped \
--memory=4g --cpus=2 \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent gateway run
What the Dockerfile does
The official image is based on debian:13.4 and includes:
- Python 3 with all Hermes dependencies (
pip install -e ".[all]") - Node.js + npm (for browser automation and WhatsApp bridge)
- Playwright with Chromium (
npx playwright install --with-deps chromium) - ripgrep and ffmpeg as system utilities
docker-cli— so agents running inside the container can drive the host's Docker daemon (bind-mount/var/run/docker.sockto opt in) fordocker build,docker run, container inspection, etc.openssh-client— enables the SSH terminal backend from inside the container. The SSH backend shells out to the systemsshbinary; without this, it failed silently in containerized installs.- The WhatsApp bridge (
scripts/whatsapp-bridge/)
The entrypoint script (docker/entrypoint.sh) bootstraps the data volume on first run:
- Creates the directory structure (
sessions/,memories/,skills/, etc.) - Copies
.env.example→.envif no.envexists - Copies default
config.yamlif missing - Copies default
SOUL.mdif missing - Syncs bundled skills using a manifest-based approach (preserves user edits)
- Then runs
hermeswith whatever arguments you pass
Upgrading
Pull the latest image and recreate the container. Your data directory is untouched.
docker pull nousresearch/hermes-agent:latest
docker rm -f hermes
docker run -d \
--name hermes \
--restart unless-stopped \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent gateway run
Or with Docker Compose:
docker compose pull
docker compose up -d
Skills and credential files
When using Docker as the execution environment (not the methods above, but when the agent runs commands inside a Docker sandbox), Hermes automatically bind-mounts the skills directory (~/.hermes/skills/) and any credential files declared by skills into the container as read-only volumes. This means skill scripts, templates, and references are available inside the sandbox without manual configuration.
The same syncing happens for SSH and Modal backends — skills and credential files are uploaded via rsync or the Modal mount API before each command.
Troubleshooting
Container exits immediately
Check logs: docker logs hermes. Common causes:
- Missing or invalid
.envfile — run interactively first to complete setup - Port conflicts if running with exposed ports
"Permission denied" errors
The container runs as root by default. If your host ~/.hermes/ was created by a non-root user, permissions should work. If you get errors, ensure the data directory is writable:
chmod -R 755 ~/.hermes
Browser tools not working
Playwright needs shared memory. Add --shm-size=1g to your Docker run command:
docker run -d \
--name hermes \
--shm-size=1g \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent gateway run
Gateway not reconnecting after network issues
The --restart unless-stopped flag handles most transient failures. If the gateway is stuck, restart the container:
docker restart hermes
Checking container health
docker logs --tail 50 hermes # Recent logs
docker run -it --rm nousresearch/hermes-agent:latest version # Verify version
docker stats hermes # Resource usage