5.2.19. Hermes Agent¶
Port: 63072 (API), 63073 (dashboard)
SOURCE variable: HERMES_SOURCE
SOURCE options: container, localhost, disabled
1. Overview¶
Hermes is a programmable AI-agent runtime from Nous Research — the missing agent loop layer between raw LLM chat and channel-specific adapters like OpenClaw. It exposes an OpenAI-compatible API on port 8642 and a web dashboard on port 9119.
Key facts:
- File-based persistence — everything lives under
/opt/data(thehermes-datanamed volume). No Postgres / Redis dependency. - No GPU required — Hermes is an orchestrator; inference is delegated to
whatever endpoint
model.base_urlpoints at. Stack default routes through the LiteLLM gateway. - MCP-native — first-class client for any MCP server.
- 64K-context floor — Hermes preflight-checks the model's context
window.
HERMES_DEFAULT_MODELMUST be a ≥64K-context model. Stock Ollama default contexts are VRAM-dependent (4k/32k/256k) and usually below 64K — setOLLAMA_CONTEXT_LENGTH=65536on the Ollama server (or/set parameter num_ctx 65536+/save <model>insideollama run), or use a cloud model. - Disk footprint — verified at ~5.66 GB on
linux/amd64andlinux/arm64(the image is multi-arch — works on Apple Silicon and standard Linux servers). Plan-time estimates put it at 2.6 GB; the actual size is over 2x that. Plan disk space accordingly. - Bundles 87 default skills — synced into
/opt/data/skills/on every container start. Ourcreative-comfyui-host-override.mdfile is added alongside them and takes precedence per Hermes's skill resolver.
2. Access¶
| Path | URL | Notes |
|---|---|---|
| OpenAI-compatible API (direct) | http://localhost:${HERMES_API_PORT} (default 63072) |
Bearer token: ${HERMES_API_KEY}. Same surface as OpenAI's /v1/chat/completions. |
| Dashboard (direct) | http://localhost:${HERMES_DASHBOARD_PORT} (default 63073) |
Web admin UI for skills, sessions, model config. |
| Dashboard (Kong) | http://hermes.localhost:63000 |
Requires ./start.sh --setup-hosts. |
| Internal DNS (other containers) | http://hermes:8642 |
Reachable from LiteLLM, n8n, jupyterhub; backend + openclaw have the env pre-wired but do not yet call it (see §10.2). |
See the canonical port table at Ports and Routes.
3. Architecture & wiring¶
Hermes is wired into the stack in two directions:
- Hermes → LiteLLM (outbound) — Hermes calls
http://litellm:4000/v1/chat/completionsfor every LLM operation. Pick the model viaHERMES_DEFAULT_MODEL(any name LiteLLM exposes). - LiteLLM → Hermes (inbound) —
services/litellm/init/scripts/init.pyappends ahermes-agentrow to LiteLLM'smodel_listwhenHERMES_SOURCE != disabled. Consequence: Open WebUI, n8n, backend, JupyterHub, OpenClaw all seehermes-agentin their model dropdowns automatically — no per-consumer wiring.
The loop is intentional. Hermes is the agent runtime above raw chat; LiteLLM is the single front door for LLM traffic.
3.1. Optional integration points (wired by hermes-init)¶
services/hermes/init/scripts/init-hermes.sh renders /opt/data/config.yaml from
environment. When the underlying service is enabled, Hermes gets:
| Hermes feature | Stack service | Mechanism |
|---|---|---|
| LLM reasoning | LiteLLM | model.provider: custom, base_url: http://litellm:4000/v1 |
| TTS (text-to-speech) | Speaches (Kokoro/Piper, default) / Chatterbox (voice cloning) | tts.provider: openai, base_url: ${TTS_ENDPOINT}/v1 — auto-set from the active TTS engine (e.g. http://speaches:8000/v1 or http://chatterbox:4123/v1) |
| STT (speech-to-text) | Speaches (Faster-Whisper, default) / Parakeet (NVIDIA NeMo) / whisper.cpp (Apple Silicon) | stt.provider: openai, base_url: ${STT_ENDPOINT}/v1, api_key: ${STT_INTERNAL_API_KEY} — all derived from the active STT engine |
| Web search | SearXNG | search.provider: searxng, base_url: http://searxng:8080 |
| Image generation | ComfyUI | Skill override at /opt/data/skills/creative-comfyui-host-override.md pinning the bundled creative-comfyui skill to http://comfyui:18188 (Hermes's default is hardcoded to 127.0.0.1:8188). |
When a dependency is disabled, the corresponding block is omitted from
config.yaml and Hermes simply doesn't expose that capability. Graceful
degradation; no failure.
4. Configuration¶
HERMES_SOURCE=container # container | localhost | disabled
HERMES_IMAGE=nousresearch/hermes-agent:v2026.6.19
HERMES_API_PORT=63072
HERMES_DASHBOARD_PORT=63073
HERMES_DASHBOARD_ENABLED=true
HERMES_DASHBOARD_TUI=1 # 1 = embed Chat tab (PTY+WS); 0 = read-only dashboard
HERMES_DEFAULT_MODEL= # blank = hermes-init auto-picks from LiteLLM's model_list
HERMES_CONTEXT_LENGTH=65536 # hard floor; leave alone
HERMES_API_KEY= # auto-generated if empty
STT_INTERNAL_API_KEY= # Parakeet token, compatibility dummy, or blank; auto-derived
HERMES_MEMORY_LIMIT=4g
HERMES_CPU_LIMIT=2.0
Auto-default model. When HERMES_DEFAULT_MODEL is blank, hermes-init
queries http://litellm:4000/v1/models at startup and picks the first
match from a priority list (ollama/qwen3.6:latest → claude-sonnet-4-6
→ claude-opus-4-7 → gpt-5 → gpt-5-codex → gpt-5-mini → first
available non-hermes-agent model). Cheapest-local-first, big-context-
cloud-second. Operator-supplied values are never overridden.
Dashboard Chat tab. With HERMES_DASHBOARD_TUI=1 (the default), the
dashboard exposes /chat and a /ws/chat WebSocket route, embedding a
PTY-backed hermes --tui session as a tab — letting you talk to the
agent directly from the web UI without going through Open WebUI or
curl. The upstream nousresearch/hermes-agent image already ships
ptyprocess for this. Set HERMES_DASHBOARD_TUI=0 for a read-only
dashboard. Reference: upstream Web-Dashboard docs.
Use ./start.sh for the guided wizard, or pass --hermes-source <option>
for scripted changes.
STT_INTERNAL_API_KEY resolves to PARAKEET_API_TOKEN only for a Parakeet source, to sk-unused for other enabled STT engines, and to empty when STT is disabled. hermes-init writes it into the server-side provider configuration; it is not a browser credential and must not be emitted in tool output.
4.1. Containerize vs. localhost¶
| Scenario | Recommended SOURCE |
|---|---|
| Hermes consumed by Open WebUI / n8n / OpenClaw (default) | container |
| Hermes operates your real machine (real shell, real browser) | localhost |
| Microphone-driven live voice mode | localhost (container mic passthrough is non-trivial) |
| Resource-constrained machine | disabled |
5. Known caveats¶
HERMES_UIDcannot be0— the upstream entrypoint runsusermod -u $HERMES_UID hermesto remap the in-container user, which fails withusermod: UID '0' already exists(root). Stack default is10000; keep it non-zero.- Gateway warning on first boot — the gateway logs
WARNING gateway.run: No user allowlists configured. All unauthorized users will be denied.This is about Hermes's messaging-platform allowlists (Telegram, Discord, etc.), NOT the OpenAI-compatible API surface. SetGATEWAY_ALLOW_ALL_USERS=truein~/.hermes/.env, or configure per-platform allowlists (TELEGRAM_ALLOWED_USERS=...,DISCORD_ALLOWED_USERS=...) when wiring messaging channels through OpenClaw. - Image tag scheme — moving
latest, immutablesha-..., dated CalVer — upstream publishes the movinglatesttag, immutablesha-<commit>tags, and datedvYYYY.M.Drelease tags (nov0.13.0-style semver). The default pinsnousresearch/hermes-agent:v2026.6.19— a concrete dated release rather than the movinglatest, so rebuilds are reproducible; production deployments can pin even tighter to a specific sha:
# In .env — pin a specific build digest
HERMES_IMAGE=nousresearch/hermes-agent:sha-e85592591e8028cceecb0ea2b4992a1643b52f93
Latest tags are listed at
https://hub.docker.com/r/nousresearch/hermes-agent/tags. If a fresh
latest introduces a regression, picking the previous sha tag is a
one-line rollback.
- ComfyUI hardcoded URL — Hermes's bundled creative-comfyui skill
defaults to 127.0.0.1:8188. We override via a skill file dropped under
/opt/data/skills/. If a workflow ignores the override, the fallback is
to add a socat sidecar mapping 127.0.0.1:8188 → comfyui:18188.
- STT base_url override is undocumented — Hermes documents base_url
override for the OpenAI TTS provider; STT may need a fallback to
provider: command with a HERMES_LOCAL_STT_COMMAND-style curl. See the
comment in services/hermes/init/templates/config.yaml.tmpl.
- 64K context floor — small Ollama models (small default contexts) will fail
Hermes's preflight check. Raise it via OLLAMA_CONTEXT_LENGTH=65536
on the Ollama server, or /set parameter num_ctx 65536 + /save <model>
inside ollama run <model>
or pick a cloud model.
- Open WebUI model-list cache — Open WebUI caches the LiteLLM model list
for 5 minutes (MODEL_LIST_CACHE_TTL=300). After first start, hermes-agent
may take up to 5 minutes to appear in the dropdown. Set
OPEN_WEB_UI_MODEL_CACHE_TTL=0 to disable while developing.
6. Integration notes¶
Depends on (must be alive for Hermes to be useful):
- LiteLLM gateway —
http://litellm:4000— Hermes refuses to operate without a reachable LLM endpoint.
Optionally consumes (wired automatically when the SOURCE != disabled):
- TTS provider (
TTS_PROVIDER_SOURCE) — Speaches / Chatterbox / disabled - STT provider (
STT_PROVIDER_SOURCE) — Speaches / Parakeet / whisper.cpp / disabled - ComfyUI (
COMFYUI_SOURCE) - SearXNG (
SEARXNG_SOURCE)
Consumed by (the hermes-agent model name appears in their dropdowns or
their env exposes HERMES_ENDPOINT):
- Open WebUI —
hermes-agentmodel in the chat dropdown (via LiteLLM). - n8n —
hermes-agentcallable from any HTTP-Request or AI node; alsoHERMES_ENDPOINTin the worker process env. - Backend API —
HERMES_ENDPOINT+HERMES_API_KEYare injected into the backend env, pre-wired for a future direct path to the agent loop; no backend code references them today, so this consumption is not yet exercised. - JupyterHub — notebooks see
HERMES_ENDPOINTfor direct calls; also thehermes-agentmodel via LiteLLM. - OpenClaw —
HERMES_ENDPOINT+HERMES_API_KEYare pre-wired for a future bridge from Hermes agents to messaging channels (WhatsApp / Telegram / Discord); per OpenClaw's README this path is not yet called.
7. References¶
- Upstream repo — https://github.com/NousResearch/hermes-agent
- Official docs — https://hermes-agent.nousresearch.com/docs/
- Open WebUI integration guide — https://hermes-agent.nousresearch.com/docs/user-guide/messaging/open-webui
- Docker / bridged-network compose form — https://hermes-agent.nousresearch.com/docs/user-guide/docker
8. RAG capability via LightRAG¶
When LIGHTRAG_SOURCE != disabled, hermes-init injects LIGHTRAG_INTERNAL_URL into /opt/data/config.yaml as a rag_query tool. Hermes can call LightRAG's /query endpoint with the configured LIGHTRAG_API_KEY. Disabled when LightRAG is off.
9. Hermes → Airflow integration¶
Hermes can trigger Airflow DAG runs via the Airflow REST API. Airflow 3.x's
public /api/v2/ uses JWT bearer tokens, not HTTP basic auth — exchange
the admin password for a JWT first, then trigger:
TOKEN=$(curl -fsS -X POST \
-H 'Content-Type: application/json' \
-d "{\"username\":\"admin\",\"password\":\"${AIRFLOW_ADMIN_PASSWORD}\"}" \
http://airflow.localhost:${KONG_HTTP_PORT}/auth/token | jq -r .access_token)
curl -fsS -X POST \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"logical_date": null, "conf": {}}' \
http://airflow.localhost:${KONG_HTTP_PORT}/api/v2/dags/example_etl_with_llm/dagRuns
This is the agent-runtime → orchestrated-workflow pattern — Hermes
detects a request, decides a long-running pipeline is needed, and
triggers an Airflow DAG. See services/airflow/README.md for the
example DAG.
10. Dependencies & Integrations¶
10.1. Current — Upstream (this service calls)¶
| Service | Category |
|---|---|
| litellm ↔ | llm |
| comfyui | media |
| searxng | media |
| stt-provider | media |
| tts-provider | media |
| airflow | agents |
| lightrag | agents |
10.2. Current — Downstream (services that call this)¶
| Service | Category |
|---|---|
| kong | infra |
| litellm ↔ | llm |
| n8n | agents |
| jupyterhub | apps |
10.3. Architecture diagram¶
Open the full-size diagram for a full-screen view.
10.4. Future — Missing pair integrations¶
- hermes ↔ neo4j — Why: Adds durable cross-session episodic memory (entities, relations) queryable from other services, replacing flat-file state under
/opt/data. Mechanism: Custom skill overbolt://neo4j-graph-db:7687exposed as amemory.graphtool. Effort: medium. Confidence: medium. - hermes ↔ weaviate — Why: Semantic recall across sessions and ingested docs, reusing the in-stack
multi2vec-clipvectorizer. Mechanism: Skill callinghttp://weaviate:8080/v1/objectsagainst aHermesMemoryclass. Effort: medium. Confidence: medium. - hermes ↔ minio — Why: Skill outputs (ComfyUI images, STT transcripts) get shareable URLs other services can fetch instead of being trapped in a bind mount. Mechanism: New
hermes-artifactsbucket via the existingminio-initIAM pattern; S3 SigV4 againsthttp://minio:9000. Effort: small. Confidence: high. - hermes ↔ n8n — Why: Reverses the current one-way edge so Hermes can invoke n8n workflows as tools, turning 400+ n8n connectors into Hermes capabilities without per-platform skills. Mechanism: Generic "call-n8n" skill POSTing to
http://n8n:5678/webhook/<id>withN8N_WEBHOOK_TOKEN. Effort: small. Confidence: high. - hermes ↔ doc-processor — Why: Lets Hermes answer questions about uploaded PDFs by routing them through the in-stack Docling parser before context or vector ingest. Mechanism: Skill POSTing multipart to
http://docling-gpu:8000/v1/document/convert. Effort: small. Confidence: high. - hermes ↔ supabase — Why: A JWT-scoped shared session store lets one Hermes session follow a user across Open WebUI, JupyterHub, and OpenClaw instead of being pinned to single-tenant
/opt/data. Mechanism: Skill writing tohermes_sessionsvia PostgREST athttp://supabase-api:3000, keyed by Supabase JWTsub. Effort: medium. Confidence: medium.
10.5. Future — Candidate new services¶
- MCP Gateway (details) — Headline: A consolidated MCP server exposing neo4j, weaviate, minio, n8n, and supabase as MCP tools any MCP-native client can mount. Wires into: hermes, open-webui, jupyterhub, neo4j, weaviate, minio, n8n.
10.6. Future — Unused features in this service¶
- MCP server mode — Why pursue: Unlocks tool-use over Neo4j/Weaviate/MinIO/n8n via a uniform protocol instead of bespoke skills, leveraging Hermes's existing MCP-client support. Effort: medium.
- Messaging-platform allowlists — Why pursue: Wiring
GATEWAY_ALLOW_ALL_USERS,TELEGRAM_ALLOWED_USERS, andDISCORD_ALLOWED_USERSis required before OpenClaw can safely bridge Hermes to Telegram/Discord/WhatsApp without an open relay. Effort: small. - Per-user / multi-tenant sessions — Why pursue: Needed for any shared deployment beyond a single developer's laptop; current
/opt/datalayout is single-tenant. Effort: large. - Voice mode (mic passthrough) — Why pursue: Enables true voice agent UX in-stack, currently gated on running Hermes via
localhostSOURCE for mic access. Effort: large. - Skill marketplace / dynamic skill install — Why pursue: Lets users add capabilities without rebuilding the image; Hermes upstream already supports dynamic skill loading. Effort: medium.
11. Troubleshooting¶
# Service status
docker compose ps hermes hermes-init
# Logs
docker compose logs -f hermes
docker compose logs hermes-init # one-shot config rendering
# Verify the OpenAI-compatible API is up
HERMES_KEY=$(grep ^HERMES_API_KEY .env | cut -d= -f2)
curl -fsS http://localhost:${HERMES_API_PORT}/v1/models \
-H "Authorization: Bearer ${HERMES_KEY}" | jq .
# Verify hermes-agent appears in LiteLLM's model_list
curl -fsS http://localhost:63040/v1/models \
-H "Authorization: Bearer ${LITELLM_MASTER_KEY}" | jq '.data[].id' | grep hermes
# Inspect the rendered config Hermes is using
docker compose exec hermes cat /opt/data/config.yaml
For general startup and routing issues, see Troubleshooting.