Guide
Hermes Agent Docker — Self-Host Hermes Agent in a Container
Hermes Agent ships as an official Docker image. This guide walks through pulling the image, wiring up env vars and persistent volumes, exposing the gateway, and standing up a production-ready self-hosted instance — with a docker-compose example you can copy and use today.
Why Docker for Hermes Agent?
Docker is the recommended way to run Hermes Agent on your own server. The official image bundles every runtime dependency — Node.js, the gateway server, all bundled plugins, and the OpenAI-compatible API layer — so there is nothing to install manually. You get a clean, reproducible environment that is easy to update, back up, and migrate.
- No dependency conflicts — the container is fully self-contained. The host only needs Docker.
- Persistent state via volumes — bind-mount
~/.hermesso config, credentials, and session memory survive container restarts and image upgrades. - Easy upgrades — pull a new tag, restart the container, and the updated binary picks up your existing data from the volume.
- Portable — the same
docker-compose.ymlworks on a VPS, a home server, or a cloud VM without modification.
The official image is hosted on GitHub Container Registry (GHCR) at github.com/NousResearch/hermes-agent. Always check the releases page for the current pinned tag before deploying — using :latest is fine for a quick test but pinning a specific version is recommended for production.
OpenClaw Launch runs Hermes Agent in a managed container for you: updates, restarts, storage and HTTPS are handled, and channels connect from the dashboard. Deploy in about 30 seconds.
Quick Start: docker run
The fastest way to get Hermes Agent running is a single docker run command. This starts the gateway on port 8642 and mounts a local directory for persistent config and memory:
# Create the data directory on your host
mkdir -p ~/.hermes
# Run the container
docker run -d \
--name hermes-agent \
--restart unless-stopped \
-p 8642:8642 \
-v ~/.hermes:/opt/data \
-e API_SERVER_ENABLED=true \
-e API_SERVER_HOST=0.0.0.0 \
-e API_SERVER_KEY=your-secret-token \
-e OPENROUTER_API_KEY=sk-or-... \
ghcr.io/nousresearch/hermes-agent:latest gateway runThe image's entrypoint runs hermes with whatever arguments you pass, so gateway run at the end is what starts the gateway. Once it is up, the OpenAI-compatible API is reachable at http://localhost:8642/v1 (health check at /health). API_SERVER_HOST=0.0.0.0 makes it listen inside the container, which it does not by default. Replace your-secret-token with a strong random string — clients send it as the API key.
:latest with a specific version tag from the upstream release page. This prevents unexpected breakage when a new version ships.Hermes Agent docker-compose
A docker-compose file is the fastest self-hosted way to run Hermes Agent — one YAML, one docker compose up -d, gateway online. The file below covers the most common production needs: persistent volume, env vars, restart policy, and an optional healthcheck.
Prerequisites
- A Linux host (Debian/Ubuntu works) with 2 GB of RAM minimum, 4 GB recommended
- Docker Engine 24+ and the compose v2 plugin
- A public hostname pointed at the host (for TLS). Not strictly required if you only want local use
- An OpenRouter / Anthropic / OpenAI API key
- A Telegram bot token if you're going to pair Telegram (most people do)
The docker-compose.yml
# docker-compose.yml
version: "3.9"
services:
hermes:
image: ghcr.io/nousresearch/hermes-agent:v2026.4.16 # pin the release you tested
container_name: hermes-agent
restart: unless-stopped
ports:
- "127.0.0.1:8642:8642" # bind to loopback; Caddy/nginx proxies HTTPS
command: ["gateway", "run"] # the entrypoint runs: hermes <args>
volumes:
- hermes-data:/opt/data # HERMES_HOME inside the image
environment:
API_SERVER_ENABLED: "true"
API_SERVER_HOST: "0.0.0.0" # listen inside the container; published on loopback only
API_SERVER_KEY: "${API_SERVER_KEY}"
OPENROUTER_API_KEY: "${OPENROUTER_API_KEY}"
# Optional — set if you use OpenAI directly
# OPENAI_API_KEY: "${OPENAI_API_KEY}"
# Optional — set if you pair a Telegram bot
# TELEGRAM_BOT_TOKEN: "${TELEGRAM_BOT_TOKEN}"
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8642/health')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
volumes:
hermes-data:Create a .env file alongside docker-compose.yml with your secrets (never commit it to git):
# .env
API_SERVER_KEY=change-me-to-a-strong-random-token
OPENROUTER_API_KEY=sk-or-v1-...Then start the stack:
docker compose up -d
docker compose logs -f hermesA healthy log ends with the gateway listening on 8642 and any configured channel platform (for example Telegram) connected. If settings, pairings or chat history vanish after a docker compose up, the data volume isn't mounted at /opt/data — fix the mount and recreate the container.
Things to call out
127.0.0.1:8642— bind to localhost only. Never publish the gateway on0.0.0.0without TLS in front.- State lives in the volume —
config.yaml,.env, chat history and the approved-user list (platforms/pairing/) all sit under/opt/data. Without the volume, recreating the container forgets every paired Telegram user and they are asked to pair again. - Pin the image — for production use a version tag (e.g.
v2026.4.16) instead of:latest.
Adding TLS with Caddy in the same compose file
Instead of running Caddy on the host, put it in the same compose file and let it auto-issue a cert. Add this service next to hermes:
caddy:
image: caddy:2
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
# add to the top-level volumes: block
volumes:
caddy_data:
caddy_config:With Caddy on the same compose network, the Caddyfile points at the service name instead of localhost:
hermes.example.com {
reverse_proxy hermes:8642
}Point DNS at the host, run docker compose up -d, and the OpenAI-compatible API is reachable at https://hermes.example.com/v1 with auto-renewing TLS (there is no web page at the root; check /health). When Caddy runs in compose, the 127.0.0.1:8642 port mapping on the Hermes service is no longer needed for it to reach the gateway.
Common docker-compose pitfalls
- Running
:latestin production — an upstream change can break your config between restarts. Always pin. - Skipping the data volume — everything looks fine until the first image upgrade or
docker rm, which wipes config, history and every Telegram pairing. - Running without a reverse proxy — the gateway speaks plain HTTP. Tokens will leak if you skip TLS.
- Forgetting to rotate the gateway auth token — generate a long random string on first boot and never commit it.
Configuration Options
Hermes Agent reads its runtime configuration from the mounted data directory (/opt/data) and from environment variables. The table below lists the most commonly used env vars at container start:
| Variable | Required | Description |
|---|---|---|
API_SERVER_KEY | Yes | Key for the OpenAI-compatible API on port 8642. Set a strong random string — clients send it as their API key. Pair it with API_SERVER_ENABLED=true and, in Docker, API_SERVER_HOST=0.0.0.0. |
OPENROUTER_API_KEY | For OpenRouter | API key for OpenRouter (routes to Claude, GPT, Gemini, DeepSeek, and others). Get one at openrouter.ai/keys. |
OPENAI_API_KEY | For OpenAI direct | Standard OpenAI API key if you want to use OpenAI models directly without OpenRouter. |
FAL_KEY | For image gen | FAL.ai API key for image generation tools. Required only if you enable the image generation skill. |
TAVILY_API_KEY | For web search | Tavily search API key. Required for the Tavily web search tool. |
PORT | No | Override the gateway listen port. Default is 8642. |
For a complete list of configuration options supported by your installed version, see the upstream Hermes Agent README.
Persistent Memory and Volumes
All of Hermes Agent's stateful data lives under /opt/data inside the container. This includes:
- config.yaml — gateway settings, model provider config, channel integrations (Telegram, Discord, Slack, etc.)
- .env and auth.json — provider API keys, bot tokens and sign-ins
- state.db and sessions/ — conversation history
- memories/ and skills/ — what the agent has learned about you and the skills it has saved
- platforms/pairing/ — approved users for Telegram, Discord and other channels
- cron/ — scheduled jobs
- logs/ — internal logs for debugging and audit
Always mount this directory to a named volume or a host path. Without a volume, every time the container is recreated (an image upgrade, docker rm, or a compose change) all keys, history and memory are gone, and every channel has to be set up and paired again.
# Named volume (recommended — Docker manages the path)
volumes:
- hermes-data:/opt/data
# Host bind mount (alternative — easier to inspect and back up)
volumes:
- ~/.hermes:/opt/dataTo back up your data, copy the volume contents to a safe location before any image upgrade:
# Back up using the named volume
docker run --rm \
-v hermes-data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/hermes-backup-$(date +%Y%m%d).tar.gz -C /data .Exposing the Gateway
The gateway runs on port 8642 inside the container. For production, do not expose it directly — put a reverse proxy with TLS in front of it. Below are minimal configs for Caddy and nginx.
Caddy (auto-HTTPS, simplest option):
# Caddyfile
hermes.yourdomain.com {
reverse_proxy localhost:8642
}Caddy automatically provisions and renews a Let's Encrypt certificate for the domain. No further TLS configuration needed.
nginx:
# /etc/nginx/sites-available/hermes
server {
listen 443 ssl;
server_name hermes.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/hermes.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hermes.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8642;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 3600s;
}
}
server {
listen 80;
server_name hermes.yourdomain.com;
return 301 https://$host$request_uri;
}The proxy_read_timeout 3600s line is important — Hermes uses long-lived WebSocket connections for real-time streaming, and the default nginx timeout of 60 seconds will disconnect active sessions.
Using a GPU for Local Models
If you run Hermes Agent with a local model backend (for example, an Ollama instance or a vLLM server on the same host), you may want to pass your GPU through to those inference containers. Hermes Agent itself does not do GPU inference — it is a gateway and orchestration layer — but it can sit alongside a GPU-accelerated inference container in the same compose stack.
For the inference container (e.g. Ollama), add the GPU deploy block:
# In docker-compose.yml — for your inference container, not Hermes itself
ollama:
image: ollama/ollama:latest
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
volumes:
- ollama-models:/root/.ollama
ports:
- "127.0.0.1:11434:11434"Then point Hermes at the local Ollama endpoint (see the Hermes + Ollama guide for full details). The --gpus all flag is the docker run equivalent of the compose snippet above.
Why Use OpenClaw Launch Instead?
Self-hosting Hermes via Docker gives you full control, but it also means managing updates, TLS, backups, restarts, and infrastructure uptime yourself. OpenClaw Launch runs the same Hermes Agent Docker container for you — fully managed.
| Self-hosted Docker | OpenClaw Launch (managed) | |
|---|---|---|
| Setup time | 30–60 minutes | ~30 seconds |
| TLS certificate | You configure Caddy or nginx | Automatic, included |
| Updates | Pull new tag, restart manually | Managed — zero downtime |
| Backups | You script and schedule | Automatic nightly backups |
| Uptime monitoring | You set up alerting | Included |
| Server cost | Your VPS ($5–$20+/mo) | From $3/mo, AI credits included |
| Docker knowledge needed | Yes | None |
OpenClaw Launch provisions the same Hermes Docker container behind the scenes. The difference is that you get a one-click deploy, a managed subdomain with HTTPS, and zero server administration — you never touch a terminal unless you want to.
Frequently Asked Questions
What port does Hermes Agent use?
The Hermes Agent gateway listens on port 8642 by default. You can override this with the PORT environment variable. For production, bind the port to 127.0.0.1:8642 and proxy it through Caddy or nginx so the port is never exposed directly to the internet.
Where does Hermes Agent store its config and memory?
All persistent data lives under /opt/data inside the container. Mount this path to a named volume or a host directory to preserve config, credentials, and session memory across container restarts and image upgrades. Without a volume, the data is lost every time the container is replaced.
Can I run multiple Hermes Agent instances on the same host?
Yes. Give each container a unique name, a separate data volume, and a different host port (for example, 8642 and 8643). Each instance has its own config, credentials, and memory — they do not share state. Use separate reverse proxy entries (or separate subdomains) to expose each one over HTTPS.
How do I update to a newer Hermes Agent version?
Pull the new image tag, then recreate the container. With docker-compose, this is a single command:
docker compose pull hermes && docker compose up -d hermesBecause the data volume is separate from the container image, your config and memory are preserved across the upgrade. Always back up the volume before a major version bump just in case.
Can I run Hermes Agent with docker-compose?
Yes. A single docker-compose.yml that mounts the Hermes data directory and binds port 8642 to localhost is enough. See the docker-compose section above, and put Caddy or nginx in front for TLS before exposing the API to the internet.
Why is Telegram dropping my messages in Hermes?
Usually the sender isn't approved yet. Hermes only answers paired users: a new sender gets a pairing code, which you approve with docker exec -u hermes -it hermes-agent /opt/hermes/.venv/bin/hermes pairing approve telegram <code>. Approvals are stored in /opt/data/platforms/pairing/, so if users have to pair again after every upgrade, the data volume isn't mounted.
Should I pin the Hermes Docker image?
Yes. Pin to a version tag (e.g. v2026.4.16) in production — an upstream change between restarts can break your config silently. Only use :latest for experimentation.
Is managed Hermes hosting available?
Yes. OpenClaw Launch offers a hosted Hermes Agent gateway with warm-pool deploy, auto-TLS, and backups, so you skip the compose file entirely.
Do I need to rebuild the image when I change config?
No. The official Hermes Agent image is pre-built. You never rebuild it — only pull a newer tag when an upstream release is available. Config changes go into the mounted data directory or as env var overrides, then take effect after a container restart.
What's Next?
- Hermes Agent Desktop App — Run Hermes locally without Docker using the native desktop installer
- Hermes Agent on Windows — Windows-specific setup: WSL2, Docker Desktop, and the native installer
- Hermes Agent + Ollama — Point Hermes at a local Ollama server for private, offline agents
- Hermes Hosting on OpenClaw Launch — Managed Hermes containers with zero server administration