Skip to content

ARCH-HERMES-AGENTS01: Hermes agents on agents01

One host, many disposable agents. The containers are cattle — the state is the bind mount. Everything an agent is (config, memory, skills, sessions, secrets) lives on the host under /opt/hermes-<name>/data; everything the container image or apt installs is reproducible from bootstrap.sh.

Host facts (agents01)

Fact Value
Public IP 92.222.245.89
Hostname agents01 (with s)
SSH access ssh -i <key> root@92.222.245.89 — root only (ubuntu@ is denied)
Resources ~8 GB RAM, 40 GB disk, Docker 29.x
Docker network hermes-net — shared by all services + nginx

Shared services on the host (not agent-owned)

Any Hermes instance reaches them via host.docker.internal (or container name on hermes-net):

Service Endpoint Use
searxng http://host.docker.internal:8888 SEARXNG_URL env (web search)
firecrawl / crw http://host.docker.internal:3000 FIRECRAWL_API_URL env (scraping)
camofox-mcp http://host.docker.internal:9378/mcp CAMOFOX_MCP_URL env (browser automation)
lightpanda / crw-camofox internal browser backends for the above

Hermes instances (validated 2026-09-01)

Instance Dashboard (host) API (host) Public URL
hermes-carlos 9123 8646 carlos.wetopi.net (HTTPS)
hermes-joan 9124 8647 joan.wetopi.net (HTTPS, LE cert)
hermes-josep 9125 8648 josep.wetopi.net (HTTPS, LE cert)

Both joan/josep share ONE certificate (live/joan.wetopi.net/, SAN: joan + josep, HTTP-01 webroot, expires 2026-11-30, auto-renew via the certbot container). Explicit A records for both subdomains → 92.222.245.89 were created 2026-09-01 (the wildcard still points at .76).

Inside every container the ports are always 9119 (dashboard) and 8642 (API server); only the host-side ports differ.

Instance layout — where persistence actually lives

/opt/hermes-<name>/data       → /opt/data      (ALL agent state, rw bind)
/opt/hermes-<name>/project    → /home/hermes   (workspace, rw bind)
/opt/hermes-<name>/.env       → API_SERVER_KEY (0600, per-instance secret)

What lives inside data/ (= the container's /opt/data): config.yaml, profile.yaml, memories/, skills/, profiles/, sessions/, state.db, secrets/, bootstrap.sh.

Consequences:

  • restart=always + Docker restarting on boot → instances survive host reboots; bind mounts remount automatically.
  • docker rm + recreate is safe: all state is on the host. What is NOT preserved is the container layer (apt packages, /usr/local/bin binaries) — bootstrap.sh (persisted inside data/) reinstalls those on start.
  • Losing data/ loses the agent. Back it up like any stateful volume.

Deploy procedure

Parametrized script at /opt/deploy-hermes-instance.sh on agents01 (validated template lives in the lead profile skill wetopi-hermes-hosts):

/opt/deploy-hermes-instance.sh <name> <dashboard_port> <api_port> start

Steps for a NEW instance (the script automates most of this):

  1. start — creates layout, generates .env, runs container (nousresearch/hermes-agent:latest, --restart always), joins hermes-net, waits for s6.
  2. Copy a known-good bootstrap.sh into the fresh data/ (from /opt/hermes-carlos/data/bootstrap.sh) and run the bootstrap action — fresh instances have none.
  3. Copy the dashboard: basic_auth block from a working instance's config.yaml and docker restart — without it the dashboard refuses to bind publicly (see pitfalls).
  4. Configure model/providers (fresh instances ship a placeholder OPENAI_API_KEY and no LLM access).
  5. Add the nginx vhost (below) and, once DNS points at .89, issue the cert and enable the HTTPS block.

nginx routing pattern

Files: /opt/composes/nginx/conf.d/<name>.conf (compose in /opt/composes/nginx/docker-compose.yml; certs volume shared with the certbot container, webroot /var/www/certbot).

  • Port 80 server: acme-challenge location + proxy_pass http://hermes-<name>:9119 with websocket upgrade headers (the dashboard needs Upgrade/Connection and long read/send timeouts).
  • Port 443 server: same proxy, ssl_certificate /etc/nginx/certs/live/<name>.wetopi.net/fullchain.pem — ship it COMMENTED until certbot has issued the cert, then uncomment and reload.
  • Wildcard *.wetopi.net currently points at 92.222.245.76; each agent subdomain needs an explicit A record → 92.222.245.89 before HTTP-01 validation can succeed.
  • Apply with docker exec nginx-proxy nginx -t && docker exec nginx-proxy nginx -s reload.

Auth requirement (dashboard)

Current hermes-agent images refuse to bind the dashboard to 0.0.0.0 without a registered auth provider. The working pattern (all three instances): dashboard.basic_auth (username + password_hash) in each instance's config.yaml. Hash via python -c "from plugins.dashboard_auth.basic import hash_password; print(hash_password('...'))". Credentials are per-agent and live only in each config.yaml — never in docs.