Back to Article List

Hermes Agent on Docker Compose with persistent state

Hermes Agent on Docker Compose with persistent state

Native install gets you running in 90 seconds. Docker Compose gets you running in five minutes but gives you something worth the extra time: clean upgrades, isolation from the host, identical setup across machines and the ability to tear the whole thing down and rebuild it with one command. If you're hosting Hermes for any reason other than personal experimentation, Compose is the boring-and-correct deployment shape.

This guide is for people who already know Docker. If you're new to containers, the native Ubuntu install is friendlier as a starting point. The LumaDock VPS setup guide covers the gentler 1-click route. Come back here once you want the operational benefits Compose brings.

What you'll end up with

One Compose file orchestrating two services: hermes (the gateway) and optionally watchtower (an automatic image updater, for the people who want auto-upgrades). State persists in a named volume so destroying and recreating the container doesn't lose your memories or skills. The container exposes ports for the dashboard and any messaging gateways that need inbound webhooks (Slack, custom HTTP).

The Compose file

Save as docker-compose.yml in a directory you control (I use /opt/hermes on production boxes):

services:
  hermes:
    image: ghcr.io/nousresearch/hermes-agent:latest
    container_name: hermes
    restart: unless-stopped
    volumes:
      - hermes-data:/root/.hermes
      - ./env:/root/.hermes/.env:ro
    environment:
      HERMES_HOME: /root/.hermes
      HERMES_LOG_LEVEL: info
      TZ: Europe/London
    ports:
      - "127.0.0.1:9119:9119"   # dashboard, bound to localhost only
      - "8765:8765"             # gateway HTTP for inbound webhooks
    mem_limit: 2g
    cpus: 2.0
    healthcheck:
      test: ["CMD", "hermes", "doctor", "--quiet"]
      interval: 60s
      timeout: 30s
      retries: 3
      start_period: 90s
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"

volumes:
  hermes-data:

A few choices worth explaining.

The image tag :latest is convenient for development. For production, pin to a specific version (:v0.12.0) so an upstream change doesn't surprise you between restarts. Bump it manually when you've tested the new version.

The dashboard port (9119) is bound to 127.0.0.1 so it's not exposed to the public internet. If you want remote access, do it through SSH tunnel or a reverse proxy with auth, not by exposing the port directly. The Nginx + HTTPS guide covers the proxy pattern.

The gateway HTTP port (8765) is exposed publicly because some channels (Slack, custom webhooks) need inbound HTTP from the internet. If you only use polling-based platforms (Telegram, Discord), drop this port mapping; you don't need it.

The ./env mount overlays a single read-only file at the path Hermes expects its .env to be. Why mount a file rather than baking the env into the Compose file directly? Because .env contains your API keys and you don't want those committed to the same git repo as your Compose file. Keep ./env in .gitignore.

The healthcheck runs hermes doctor --quiet every 60 seconds. The flag suppresses normal output, so the check succeeds silently if all checks pass and exits non-zero if anything is broken. The 90-second start_period gives Hermes time to load skills and connect to providers on first start without the healthcheck flapping.

The log rotation cap (10 MB per file, 5 files) keeps Docker's JSON log driver from filling your disk. For most installs this gives you about a week of logs at warning level or above.

Bring it up

cd /opt/hermes
echo "TELEGRAM_BOT_TOKEN=your-token-here" > env
echo "OPENROUTER_API_KEY=your-key-here" >> env
chmod 600 env
docker compose up -d
docker compose logs -f hermes

The logs should show Hermes loading providers, connecting to your messaging platforms and the healthcheck flipping to healthy after about 90 seconds. If the container restarts in a loop, the logs tell you why; the most common cause on first start is a missing or malformed env file.

To shell into the container:

docker compose exec hermes bash

From inside, hermes is on the PATH and behaves exactly as it would on a native install. Useful when you want to tweak config or run an ad-hoc command without bouncing the gateway.

Persistent state in detail

The named volume hermes-data mounts at /root/.hermes inside the container. That's where everything lives: the SQLite session database, the skills directory, the memory files. Destroying the container with docker compose down leaves the volume intact; only docker compose down -v wipes it.

To inspect what's in the volume:

docker run --rm -v hermes_hermes-data:/data alpine ls -la /data

(Note the volume name is prefixed with the project name, which is the directory name by default. Adjust if your Compose project is named differently.)

To back up the volume to a tarball, see the dedicated backups guide. The short version is "tar -czf the volume contents from a sidecar container".

Upgrades and rollbacks

The clean upgrade flow:

cd /opt/hermes
docker compose pull
docker compose up -d
docker compose logs -f hermes

pull fetches the new image. up -d recreates the container against the new image, preserving the volume. The named volume is what makes this safe; the container is disposable, the volume is persistent.

If the new version misbehaves, roll back by changing the image tag to a known-good version and running up -d again. Because state is in the volume rather than baked into the image, downgrades work as cleanly as upgrades.

For automatic upgrades during a maintenance window, add a Watchtower service to the Compose file:

  watchtower:
    image: containrrr/watchtower
    container_name: watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      WATCHTOWER_CLEANUP: "true"
      WATCHTOWER_SCHEDULE: "0 0 4 * * *"   # 04:00 daily
      WATCHTOWER_INCLUDE_RESTARTING: "true"
      WATCHTOWER_NOTIFICATION_URL: "your-webhook-here"

Watchtower polls Docker Hub at 04:00, pulls if the image has changed and gracefully restarts the container with the new image. The notification URL accepts Discord, Slack and many others; it pings you when an upgrade happens, so you're not surprised if something breaks at 4 AM.

Enabling automatic upgrades depends on your tolerance for unattended changes. For a personal agent, fine. For a production agent with users, the safer pattern is manual upgrades after testing in a staging environment.

Resource sizing

The Compose example caps memory at 2 GB and CPU at 2 cores. These are reasonable starting points; tune to your load.

If you're doing browser automation (the browser_ tool family inside Hermes), each Playwright session can spike to 500 MB to 1 GB of memory on its own. If your agent runs concurrent browser sessions, lift the cap to 4 GB or risk the kernel killing the container during a heavy task.

If you're doing only chat-based work with no browser, no heavy file processing, the 2 GB cap is conservative; you could probably run on 1 GB. The trade-off is that going lower starts to bite when your skill set grows. If you're sized too tight, you'll get OOM-killed mid-conversation and the user-visible symptom is "the bot stopped responding partway through a message".

CPU caps matter less than memory in practice. Most Hermes work is IO-bound on the LLM call, so CPU usage is bursty during local processing (parsing tool outputs, running code execution skills) and idle the rest of the time. Two cores is plenty for one user; bump to four if you have many concurrent users on the same instance.

Networking notes

If you're running Hermes alongside other services on the same box (a PaaS, a workflow tool, an analytics stack), put them on a shared Docker network so they can talk to each other without going through the host:

networks:
  agent-stack:
    external: true

Then add networks: [agent-stack] to the hermes service. Other services on the same external network can reach Hermes by container name (http://hermes:9119) without exposing the port to the host at all.

If you're using a reverse proxy (Nginx, Caddy, Traefik) in another Compose stack, the cleanest pattern is to put the proxy and Hermes on a shared external network and have the proxy talk to Hermes by container name. The proxy publishes 80/443 to the host; Hermes publishes nothing.

Migration from native install to Docker

If you've been running Hermes natively and want to move to Compose without losing state, the trick is to copy your existing ~/.hermes contents into the Compose volume.

# Stop the native service first
sudo systemctl stop hermes

# Bring up the Compose stack briefly to create the volume
cd /opt/hermes
docker compose up -d
docker compose stop hermes

# Copy state in
docker run --rm \
  -v hermes_hermes-data:/dst \
  -v /home/youruser/.hermes:/src \
  alpine sh -c "cp -av /src/. /dst/"

# Start Hermes pointing at the migrated state
docker compose start hermes
docker compose logs -f hermes

Verify the agent remembers your context (a project name, a skill, anything that was in your native memory). If it does, the migration worked; you can disable the native systemd service and remove the native install when you're ready.

The 1-click route

If you'd rather skip writing the Compose file yourself, the LumaDock Hermes Agent VPS template ships with the agent already installed natively on Ubuntu 24.04 plus Docker CE pre-installed, so you can switch to a Compose deployment any time. The native install gets you answering messages faster than the Compose file would; you'd reach for Compose later if you outgrow the basics or want to bundle Hermes with other services on the same box.

Your idea deserves better hosting

24/7 support 30-day money-back guarantee Cancel anytime
Faktureras

1 GB RAM VPS

36.89 kr Save  25 %
27.64 kr Månadsvis
  • 1 vCPU AMD EPYC
  • 30 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

2 GB RAM VPS

55.38 kr Save  17 %
46.13 kr Månadsvis
  • 2 vCPU AMD EPYC
  • 30 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

6 GB RAM VPS

138.58 kr Save  33 %
92.36 kr Månadsvis
  • 6 vCPU AMD EPYC
  • 70 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

AMD EPYC VPS.P1

73.87 kr Save  25 %
55.38 kr Månadsvis
  • 2 vCPU AMD EPYC
  • 4 GB RAM-minne
  • 40 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

AMD EPYC VPS.P2

138.58 kr Save  27 %
101.60 kr Månadsvis
  • 2 vCPU AMD EPYC
  • 8 GB RAM-minne
  • 80 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

AMD EPYC VPS.P4

277.25 kr Save  20 %
221.78 kr Månadsvis
  • 4 vCPU AMD EPYC
  • 16 GB RAM-minne
  • 160 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

AMD EPYC VPS.P5

337.34 kr Save  21 %
268.01 kr Månadsvis
  • 8 vCPU AMD EPYC
  • 16 GB RAM-minne
  • 180 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

AMD EPYC VPS.P6

526.86 kr Save  21 %
415.92 kr Månadsvis
  • 8 vCPU AMD EPYC
  • 32 GB RAM-minne
  • 200 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

AMD EPYC VPS.P7

647.04 kr Save  20 %
517.62 kr Månadsvis
  • 16 vCPU AMD EPYC
  • 32 GB RAM-minne
  • 240 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

EPYC Genoa VPS.G1

46.13 kr Save  20 %
36.89 kr Månadsvis
  • 1 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4:e generation 9xx4 med 3,25 GHz eller liknande, baserad på Zen 4-arkitekturen.
  • 1 GB DDR5 RAM-minne
  • 25 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

EPYC Genoa VPS.G2

120.09 kr Save  23 %
92.36 kr Månadsvis
  • 2 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4:e generation 9xx4 med 3,25 GHz eller liknande, baserad på Zen 4-arkitekturen.
  • 4 GB DDR5 RAM-minne
  • 50 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

EPYC Genoa VPS.G4

240.27 kr Save  27 %
175.56 kr Månadsvis
  • 4 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4:e generation 9xx4 med 3,25 GHz eller liknande, baserad på Zen 4-arkitekturen.
  • 8 GB DDR5 RAM-minne
  • 100 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

EPYC Genoa VPS.G6

452.90 kr Save  31 %
314.23 kr Månadsvis
  • 8 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4:e generation 9xx4 med 3,25 GHz eller liknande, baserad på Zen 4-arkitekturen.
  • 16 GB DDR5 RAM-minne
  • 200 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

EPYC Genoa VPS.G7

693.27 kr Save  27 %
508.37 kr Månadsvis
  • 8 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4:e generation 9xx4 med 3,25 GHz eller liknande, baserad på Zen 4-arkitekturen.
  • 32 GB DDR5 RAM-minne
  • 250 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

1 vCPU AMD Ryzen 9

129.33 kr Save  29 %
92.36 kr Månadsvis
  • Dedikerad CPU 4.5GHz AMD Ryzen 9 7950X med en inbyggd CPU-frekvens på 4.5 GHz.
  • 4 GB DDR5 RAM-minne
  • 50 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

2 vCPU AMD Ryzen 9

240.27 kr Save  19 %
194.05 kr Månadsvis
  • Dedikerad CPU 4.5GHz AMD Ryzen 9 7950X med en inbyggd CPU-frekvens på 4.5 GHz.
  • 8 GB DDR5 RAM-minne
  • 100 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

8 vCPU AMD Ryzen 9

859.67 kr Save  30 %
600.82 kr Månadsvis
  • Dedikerad CPU 4.5GHz AMD Ryzen 9 7950X med en inbyggd CPU-frekvens på 4.5 GHz.
  • 32 GB DDR5 RAM-minne
  • 400 GB NVMe lagring
  • Obegränsad bandbredd
  • IPv4 och IPv6 ingår IPv6-stöd är för närvarande inte tillgängligt i Frankrike, Finland eller Nederländerna.
  • 1 Gbps nätverk
  • Automatisk backup ingår
  • Hantering av brandvägg
  • Kostnadsfri serverövervakning

FAQ

How do I run a Hermes interactive CLI session against a container?

docker compose exec hermes hermes drops you into an interactive Hermes session inside the container, sharing state with the gateway. The two processes coexist fine because they read the same memory files but have separate session DBs entries. If you'd rather not exec into the container every time, docker compose run --rm hermes hermes spins up a one-shot container with the same volume mounted, gives you a CLI and tears down when you exit.

Your agent runs wild. Your bill doesn't.

Easily deploy Hermes in one click on Ubuntu 24.04 with AMD EPYC, NVMe storage and unmetered bandwidth. The price stays the same whatever the agent does, no setup fees, no overage charges and no tier traps.

GPU products are in high demand at the moment. Fill the form to get notified as soon as your preferred GPU server is back in stock.