↓ Skip to main content

Deploying Hermes Agent with OmniRoute for Free and Cheap Tokens

·14 mins
Omar Amin
Author
Omar Amin
Loves boxing, FOSS and Selfhosting
Table of Contents

In the previous post I laid out why the $20-30 tier is the sweet spot for personal AI agents: self-host the loop (Hermes), subscribe to the model (Claude Code, Gemini Tasks, or metered API). The missing piece was the how — how to actually wire a self-hosted Hermes instance to a model router that gives you free tiers and cheap paid tokens without juggling ten API keys.

This post is that how-to. It’s based on the stack running on my host today: Hermes Agent + OmniRoute + Redis in a docker compose file, with OmniRoute configured to route requests through DeepSeek (paid) and a 13-step free-tier cascade across 6 providers. All on a private Docker network, no public exposure, Matrix as the capture surface.

Note: The compose file and configs below are a trimmed version of what I run. The deployed stack also includes an API gateway, a per-profile secret-entry listener for each profile, and a Gitea webhook relay — omitted here for clarity. The core four services (Redis, OmniRoute, Hermes, browser) are byte-identical.

The architecture

+-----------------------------------------------------------------+
|                        Your Host (your-host)                      |
|  +--------------+    +--------------+    +------------------+  |
|  |   Hermes     |--->|  OmniRoute   |--->|      Redis       |  |
|  |  (agent)     |    |  (gateway)   |    |  (rate limit/    |  |
|  |  port 9119   |    |  port 20128  |    |   cache)         |  |
|  +--------------+    +--------------+    +------------------+  |
|        |                    |                                    |
|        |                    v                                    |
|        |            +--------------+                             |
|        |            |   Providers  |                             |
|        |            |  (external)  |                             |
|        |            |  * DeepSeek  |                             |
|        |            |  * NIM       |                             |
|        |            |  * OpenRouter|                             |
|        |            |  * Z.AI      |                             |
|        |            |  * Cloudflare|                             |
|        |            |  * Gemini    |                             |
|        |            |  * Mistral   |                             |
|        |            +--------------+                             |
|        v                                                         |
|  +--------------+                                                |
|  |   Matrix     |                                                |
|  |  (Tuwunel)   |                                                |
|  +--------------+                                                |
+-----------------------------------------------------------------+

Key design decisions:

  • Private network only — aitools_internal is an internal Docker network. OmniRoute binds to your host’s LAN IP (e.g., 192.168.x.x:20128), never 0.0.0.0. No Traefik route, no port forwarding. Hermes reaches OmniRoute at http://omniroute:20128/v1 over the container name.
  • Redis is used for distributed rate limiting and shared cache. The compose includes a tuned Redis sidecar (128MB maxmemory, LRU eviction).
  • Secrets in .env, not the compose file — JWT_SECRET, API_KEY_SECRET, STORAGE_ENCRYPTION_KEY, INITIAL_PASSWORD, OMNIROUTE_WS_BRIDGE_SECRET, and the OmniRoute API key for Hermes all live in a .env file that the compose file references.
  • Require API key on every request — REQUIRE_API_KEY=true means Hermes must present an OmniRoute key. The key is pinned to a specific routing combo (see below).

The docker compose file

Save this as docker-compose.yml on your Docker host. The ${VAR} placeholders come from a .env file in the same directory (see the .env.example section below).

# ──────────────────────────────────────────────────────────────────────
#  Hermes Agent + OmniRoute + Redis — single-host, LAN-only (trimmed)
#  Tested: Hermes v0.22.5, OmniRoute 3.8.52, Redis 8.6.7-alpine
# ──────────────────────────────────────────────────────────────────────

services:
  omniroute-redis:
    image: docker.io/library/redis:8.6.7-alpine@sha256:7033ad495c19b6bca38aea38f9130d1a3d5b647ac94f3ebe644c8c0fb804e039
    container_name: omniroute-redis
    restart: unless-stopped
    command:
      - "redis-server"
      - "--save"
      - "60"
      - "1"
      - "--appendonly"
      - "no"
      - "--maxmemory"
      - "128mb"
      - "--maxmemory-policy"
      - "allkeys-lru"
    volumes:
      - ./data/omniroute-redis:/data
    networks: [aitools_internal]
    # no ports: never publish (no requirepass)
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 30s
      timeout: 5s
      retries: 3
    mem_limit: 192m
    cpus: 0.25
    security_opt: ["no-new-privileges:true"]

  omniroute:
    image: docker.io/diegosouzapw/omniroute:3.8.52@sha256:b08a12e876baa8a5ccadd1ebbe346fe7267dba3b1c9f4fd6bb02070fae41ab6c
    container_name: omniroute
    restart: unless-stopped
    stop_grace_period: 40s          # SQLite WAL checkpoint (upstream guidance)
    depends_on:
      omniroute-redis:
        condition: service_healthy
    environment:
      # secrets from .env (only the OmniRoute ones are passed in)
      JWT_SECRET: ${JWT_SECRET}
      API_KEY_SECRET: ${API_KEY_SECRET}
      STORAGE_ENCRYPTION_KEY: ${STORAGE_ENCRYPTION_KEY}
      INITIAL_PASSWORD: ${INITIAL_PASSWORD}
      OMNIROUTE_WS_BRIDGE_SECRET: ${OMNIROUTE_WS_BRIDGE_SECRET}
      PORT: "20128"
      HOSTNAME: "0.0.0.0"
      DATA_DIR: /app/data
      REDIS_URL: redis://omniroute-redis:6379
      OMNIROUTE_MEMORY_MB: "1536"   # chat-completions only (Hermes); /v1/responses coding agents need 8192+
      REQUIRE_API_KEY: "true"       # every /v1 call needs an OmniRoute key (Hermes gets its own)
      AUTH_COOKIE_SECURE: "${OMNIROUTE_COOKIE_SECURE:-false}"   # true only if dashboard is served via Traefik/HTTPS
      NEXT_PUBLIC_BASE_URL: "${OMNIROUTE_PUBLIC_URL:-http://YOUR_LAN_IP:20128}"
      TZ: ${TZ}
    volumes:
      - ./data/omniroute:/app/data
    networks: [aitools_internal]
    ports:
      # LAN-only: bind to your host's LAN IP, never 0.0.0.0, no public Traefik route by default.
      # Router must NOT port-forward 20128.
      - "YOUR_LAN_IP:20128:20128"
    healthcheck:
      test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:20128/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
      interval: 60s
      timeout: 15s
      retries: 5
      start_period: 60s
    mem_limit: 2560m
    cpus: 1.5
    security_opt: ["no-new-privileges:true"]
    labels:
      - homepage.group=AI
      - homepage.name=OmniRoute
      - homepage.href=http://YOUR_LAN_IP:20128

  hermes:
    image: docker.io/nousresearch/hermes-agent:v0.22.5@sha256:9c5561579f3ffdf333401797094ac3a743738d6c25d40f4331c7bf77b694238d
    container_name: hermes
    restart: unless-stopped
    command: gateway run            # s6 supervises gateway(s) per profile + dashboard
    stop_grace_period: 60s
    depends_on:
      omniroute:
        condition: service_started
    environment:
      HERMES_UID: "1000"            # match your host UID for the bind mount
      HERMES_GID: "1000"
      HERMES_DASHBOARD: "1"
      HERMES_DASHBOARD_HOST: "0.0.0.0"
      HERMES_DASHBOARD_PORT: "9119"
      HERMES_DASHBOARD_PUBLIC_URL: "https://hermes.${DOMAINNAME}"
      # Enable browser tools (web search, auth flows) — requires the optional 'browser' service below
      BROWSER_CDP_URL: "http://browser.localhost:9222"
      # Dashboard auth gate is mandatory on non-loopback binds. Basic auth inside + Authelia in front.
      HERMES_DASHBOARD_BASIC_AUTH_USERNAME: ${HERMES_DASHBOARD_BASIC_AUTH_USERNAME}
      # Password: scrypt hash in /opt/data/config.yaml (host only). Do NOT set HERMES_DASHBOARD_BASIC_AUTH_PASSWORD (env plaintext wins over the hash).
      HERMES_DASHBOARD_BASIC_AUTH_SECRET: ${HERMES_DASHBOARD_BASIC_AUTH_SECRET}
      # API server (8642) is gated by API_SERVER_KEY in v0.22.5, not API_SERVER_ENABLED.
      # The example below does not set a key, so the server stays off.
      TZ: ${TZ}
      # Provider keys, Matrix tokens etc. go in /opt/data/.env and /opt/data/profiles/<p>/.env (NOT here).
    volumes:
      - ./data/hermes:/opt/data
      # If you use secure-env-ingress for secret entry, mount its LAN cert:
      # - ./data/hermes/profiles/your-paid-profile/.senv/senv-cert.pem:/etc/ssl/certs/bbbbc909.0:ro
      #   Generate: docker run --rm -v ./data/hermes/profiles/your-paid-profile/.senv:/out ghcr.io/omaramin/secure-env-ingress:latest gencert --host YOUR_LAN_IP
      # NO docker socket. NO ~/.ssh. Terminal backend = local (inside this container only).
    networks: [aitools_internal]
    ports:
      - "YOUR_LAN_IP:9443:9443"   # secure-env-ingress HTTPS form (LAN only, bearer one-use link)
      - "YOUR_LAN_IP:9119:9119"   # dashboard -> Traefik (reverse proxy) with Authelia
      # 8642 intentionally NOT published (no API_SERVER_KEY set)
    healthcheck:
      test: ["CMD", "/opt/hermes/.venv/bin/python", "-c", "import socket; socket.create_connection(('127.0.0.1', 9119), 5)"]
      interval: 60s
      timeout: 15s
      retries: 3
      start_period: 120s
    mem_limit: 3g
    cpus: 2.0
    pids_limit: 2048                # 512 was hit on 30 Oct 2026 (Chromium threads); deployed uses 2048
    ulimits:
      core: 0                       # prevent 1+ GB crash dumps from chrome_crashpad
    security_opt: ["no-new-privileges:true"]
    labels:
      - homepage.group=AI
      - homepage.name=Hermes
      - homepage.href=https://hermes.yourdomain

  # Optional: shared headless Chromium for browser tools (web search, auth flows)
  # Hermes v0.22.5 attaches via BROWSER_CDP_URL = http://browser.localhost:9222
  # The ".localhost" alias matters: Chrome's DevTools HTTP server rejects any Host header that is not an IP or localhost/*.localhost
  browser:
    image: docker.io/chromedp/headless-shell:157.0.8103.15@sha256:f76fce2964611eb1e25b90d906727884db12a8918a6d85a0df2619a9ef845c7c
    container_name: browser
    restart: unless-stopped
    init: true
    user: "1000:1000"
    command:
      - --user-data-dir=/data/profile
      - --no-first-run
      - --no-default-browser-check
      - --disable-crash-reporter
      - --lang=en-US
    environment:
      HOME: /data
      TZ: ${TZ}
    volumes:
      - ./data/browser:/data
    networks:
      aitools_internal:
        aliases: [browser.localhost]
    # NO ports: CDP = full control of the browser and its logged-in sessions. No Traefik route.
    shm_size: 1gb
    mem_limit: 2g
    cpus: 1.5
    pids_limit: 2048
    security_opt: ["no-new-privileges:true"]
    healthcheck:
      test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/127.0.0.1/9222 && printf 'GET /json/version HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' >&3 && grep -q webSocketDebuggerUrl <&3"]
      interval: 60s
      timeout: 10s
      retries: 3
      start_period: 30s

networks:
  aitools_internal:
    name: aitools_internal
    internal: false          # hermes/omniroute need egress to LLM providers + Matrix

The .env.example file

Create a .env file alongside the compose file with these variables. Never commit the real .env to git.

# ──────────────────────────────────────────────────────────────────────
#  Hermes + OmniRoute .env — copy to .env and fill in
# ──────────────────────────────────────────────────────────────────────

# Timezone (Asia/Dubai for me)
TZ=Asia/Dubai

# Your base domain for public URLs (used in Hermes dashboard public URL)
DOMAINNAME=yourdomain

# ────────── OmniRoute secrets (generate with: openssl rand -base64 32) ──────────
JWT_SECRET=                        # 32+ chars, used for JWT signing
API_KEY_SECRET=                    # 32+ chars, encrypts stored provider keys
STORAGE_ENCRYPTION_KEY=            # 32+ chars, encrypts SQLite database
INITIAL_PASSWORD=                  # OmniRoute dashboard initial admin password
OMNIROUTE_WS_BRIDGE_SECRET=        # 32+ chars, WebSocket bridge auth

# OmniRoute dashboard cookie security (true only if behind HTTPS/Traefik)
OMNIROUTE_COOKIE_SECURE=false
# Public URL for OmniRoute dashboard (used in OAuth redirects, etc.)
OMNIROUTE_PUBLIC_URL=http://YOUR_LAN_IP:20128

# ────────── Hermes dashboard auth (basic auth + Authelia in front) ──────────
HERMES_DASHBOARD_BASIC_AUTH_USERNAME=admin
# Generate scrypt hash: docker run --rm nousresearch/hermes-agent:v0.22.5 python -c "import scrypt; print(scrypt.hash(b'YOUR_PASSWORD', scrypt.gensalt()).decode())"
HERMES_DASHBOARD_BASIC_AUTH_SECRET=    # 32+ chars, token signing key

Generate the secrets once:

openssl rand -base64 32   # run this 5 times for the 5 OmniRoute secrets

Bringing it up

# 1. Create the .env from the example and fill in secrets
cp .env.example .env
# edit .env with your values

# 2. Create data directories with correct ownership (UID 1000)
mkdir -p data/omniroute-redis data/omniroute data/hermes data/browser
chown -R 1000:1000 data/

# 3. Pull images and start
docker compose pull
docker compose up -d

# 4. Verify all containers are healthy
docker compose ps
# Check logs if anything is unhealthy
docker compose logs -f omniroute
docker compose logs -f hermes

Configuring OmniRoute: providers, free tiers, and routing combos

Once the stack is up, open the OmniRoute dashboard at http://YOUR_LAN_IP:20128 (or your LAN IP). Log in with the INITIAL_PASSWORD from your .env.

Step 1: Add providers

Go to Providers in the sidebar. Add providers that suit your needs. The running instance uses these (all have some form of free access; check each provider’s current terms):

ProviderNotes
DeepSeekPrepaid; no recurring free tier. 5M tokens on signup (one-time).
Gemini (Google AI Studio)Recurring free quota on Flash models.
NVIDIA NIMFree tier on select models (e.g., Nemotron).
OpenRouterFree models via *:free slugs (e.g., nemotron-3-ultra-550b-a55b:free).
Z.AI / GLMFree tier on GLM models.
Cloudflare Workers AIFree tier with Cloudflare account.
MistralFree tier on select models (e.g., ministral-14b-latest).

For each: click Add Provider, choose the provider, paste the API key, save. OmniRoute encrypts the keys at rest using STORAGE_ENCRYPTION_KEY.

Note: Free-tier details change. Check each provider’s current pricing page and OmniRoute’s Free Tiers Guide before relying on specific quotas.

Step 2: Create routing combos

OmniRoute routes requests through combos — named routing policies that define which providers to try, in what order, and with what strategy. Go to Combos and create these two:

hermes-agent-private (paid DeepSeek only)

  • Strategy: priority
  • Providers: deepseek (only)
  • Model: deepseek/deepseek-v4-flash (forced via forcedModel on the API key)
  • Purpose: The paid tier for profiles that handle sensitive data (your paid-tier profiles). This combo excludes all free tiers — it’s a hard privacy boundary.

hermes-agent (free tier cascade)

  • Strategy: priority
  • Providers (13 steps across 6 providers, in order):
    1. nvidia — nemotron-3-ultra-550b-a55b
    2. gemini — gemini-3.8-flash
    3. openrouter — nemotron-3-ultra-550b-a55b:free
    4. zai — glm-4.7-flash
    5. cloudflare-ai — @cf/zai-org/glm-4.7-flash
    6. gemini — gemini-3-flash-preview
    7. openrouter — nemotron-3.5-super-vl:free
    8. gemini — gemini-3.6-flash
    9. mistral — ministral-14b-latest
    10. gemini — gemini-3.5-flash-lite
    11. openrouter — laguna-s-2.1:free
    12. nvidia — minimax-m3.1-flash
    13. openrouter — gemma-4-31b-it:free
  • Purpose: General-purpose agent work where free tiers are fine. No DeepSeek step — this is a “FREE ONLY” combo by design. If a step is priced in the gateway’s rate table (e.g., gemini-3.8-flash shows $0.75/$3.75), the gateway may still estimate a cost, but no vendor bill is generated for the free-tier models.

Step 3: Create API keys for Hermes

Go to Endpoints → Create API Key. Create one key per tier, not per profile — several profiles can share a key:

Key namePinned comboSpend limitUsed by profiles
your-paid-keyhermes-agent-private(not set)your paid-tier profiles (e.g., your-paid-profile, your-other-paid-profile)
your-free-keyhermes-agent(not set)your free-tier profiles (e.g., your-free-profile, your-other-free-profile, …)

Important: When creating the key, set Pinned Combo to the combo name. This forces every request using that key through that specific routing policy. The key cannot access other combos.

Note: The key named your-free-key is pinned to the free combo (hermes-agent). The paid profiles use your-paid-key pinned to hermes-agent-private. The pin is what enforces the tier, not the key name — a wrong pin looks like success (HTTP 200) while a different provider answers.

Copy each key — you’ll need them in the Hermes profile .env files.

Configuring Hermes: pointing at OmniRoute

Hermes treats OmniRoute as a custom OpenAI-compatible endpoint. The config lives in each profile’s config.yaml (under /opt/data/profiles/<profile>/config.yaml in the container, or ./data/hermes/profiles/<profile>/config.yaml on the host).

Profile using paid DeepSeek only (your paid-tier profiles)

# /opt/data/profiles/your-paid-profile/config.yaml
providers:
  omniroute:
    api: http://omniroute:20128/v1          # internal Docker network; never via Traefik
    key_env: OMNIROUTE_API_KEY               # OmniRoute key "your-paid-key", PINNED to hermes-agent-private
    discover_models: false
    models:
      - hermes-agent-private                 # paid-only combo: deepseek/deepseek-v4-flash

model:
  default: hermes-agent-private
  provider: custom:omniroute

# If OmniRoute itself is down/erroring, go straight to DeepSeek (small prepaid balance = hard cap)
fallback_providers:
  - provider: deepseek                       # uses DEEPSEEK_API_KEY from profile .env
    model: deepseek-flash

In the profile’s .env:

OMNIROUTE_API_KEY=omr_...   # the "your-paid-key" key from OmniRoute Endpoints page
DEEPSEEK_API_KEY=sk-...     # direct DeepSeek key for fallback (optional but recommended)
MATRIX_HOMESERVER=http://YOUR_MATRIX_HOMESERVER:8008
MATRIX_ACCESS_TOKEN=...
MATRIX_USER_ID=@your-paid-bot:matrix.yourdomain
MATRIX_DEVICE_ID=YOUR_PAID_DEVICE
MATRIX_E2EE_MODE=optional
MATRIX_RECOVERY_KEY=...

Note: If you have multiple paid profiles, they can share this config and key. Add DEEPSEEK_API_KEY to each profile’s .env if you want the direct fallback to work.

Profile using free tier cascade (your free-tier profiles)

# /opt/data/profiles/your-free-profile/config.yaml
providers:
  omniroute:
    api: http://omniroute:20128/v1
    key_env: OMNIROUTE_API_KEY               # OmniRoute key "your-free-key", PINNED to hermes-agent
    discover_models: false
    models:
      - hermes-agent                         # free tier cascade: 13 steps across 6 providers

model:
  default: hermes-agent
  provider: custom:omniroute
  context_length: 128000

# No fallback_providers here — if OmniRoute is down, the request errors.
# Add DEEPSEEK_API_KEY to .env and the fallback block above if you want a direct fallback.

In the profile’s .env:

OMNIROUTE_API_KEY=omr_...   # the "your-free-key" key from OmniRoute Endpoints page
# NO DEEPSEEK_API_KEY — this profile stays on free tiers only
MATRIX_HOMESERVER=http://YOUR_MATRIX_HOMESERVER:8008
MATRIX_ACCESS_TOKEN=...
MATRIX_USER_ID=@your-free-bot:matrix.yourdomain
MATRIX_DEVICE_ID=YOUR_FREE_DEVICE
MATRIX_E2EE_MODE=optional
MATRIX_RECOVERY_KEY=...

Creating the Hermes profiles

# Enter the running Hermes container
docker exec -it hermes bash

# Create each profile
hermes profile create your-paid-profile
hermes profile create your-free-profile
hermes profile create your-other-free-profile
# ... add others as needed

# Validate each
hermes -p your-paid-profile doctor
hermes -p your-free-profile doctor
hermes -p your-other-free-profile doctor

The doctor command diagnoses issues with Hermes Agent setup. With --live it can probe tool backends; without, it checks config and static connectivity.

Testing the chain

With everything running, test from the Hermes container:

# Test OmniRoute connectivity from inside Hermes container
docker exec -it hermes bash -c 'curl -s http://omniroute:20128/v1/models -H "Authorization: Bearer $OMNIROUTE_API_KEY" | head -c 400'

# Test a chat request through the Hermes profile
docker exec -it hermes hermes -p your-free-profile chat --oneshot "Say hello in one sentence"

Watch the OmniRoute dashboard — you should see the request arrive, which provider it routed to, latency, and estimated cost.

Matrix as the capture surface

This setup uses Matrix (e.g., Synapse or Tuwunel) as the capture surface, not Telegram. Each Hermes profile connects to Matrix with its own device ID and access token. The matrix section in config.yaml gates access by user mention — only you and your bot IDs can invoke the agent.

matrix:
  thread_require_mention: true
  require_mention: true
  allowed_users:
    - '@you:matrix.yourdomain'
    - '@your-paid-bot:matrix.yourdomain'
    - '@your-free-bot:matrix.yourdomain'
    # ... add your other bot IDs
  allowed_rooms: []        # empty = no room restriction; mention gating applies
  process_notices: false
  session_scope: room
  auto_thread: true

To interact: create a Matrix room, invite @your-paid-bot:matrix.yourdomain (or whatever profile), and mention it: @your-paid-bot what's the status of my stack?

What this costs (rough guide)

ComponentMonthly cost
Hardware (already owned)$0
DeepSeek prepaid balanceSingle-digit dollars for light use; tens for a busy estate
Free-tier providers (Gemini, NIM, OpenRouter free models, Z.AI, Cloudflare, Mistral)$0 (no vendor bill on free steps; gateway may still estimate)
OmniRoute / Redis / Hermes$0 (self-hosted)

Compare to $20-30/month for a managed agent tier, or $200 for business tiers. The trade-off is one Saturday of wiring — after that, you own the control plane, the data, and the routing logic.

Common pitfalls

SymptomCauseFix
Hermes can’t reach http://omniroute:20128/v1Containers not on same networkVerify both are on aitools_internal (docker network inspect aitools_internal)
OmniRoute dashboard returns 401/403REQUIRE_API_KEY=true but no key sentEnsure Hermes profile has OMNIROUTE_API_KEY in .env and key_env: OMNIROUTE_API_KEY in config
“No models available” on custom endpointdiscover_models: false but model not in models: listAdd the combo model name (e.g., hermes-agent-private) to the models: array
Free tier step skippedProvider hit quota or returned errorThe cascade combo (hermes-agent) automatically falls through to the next step
SQLite “database is locked” on OmniRoute restartstop_grace_period too shortKeep stop_grace_period: 40s — lets SQLite checkpoint WAL
hermes chat --once failsWrong flagUse --oneshot instead
Thread exhaustion in Hermes containerpids_limit too lowUse 2048 (512 was hit on 30 Oct 2026)

What’s next

  • Add more free providers — Logfare (free key, no rate limits), Kiro AI, Pollinations. Each adds fallback depth.
  • Routing policies per task — Use auto/coding, auto/fast, auto/cheap aliases in OmniRoute for automatic model selection based on the request type.
  • Cost alerts — OmniRoute’s dashboard shows per-key spend. Set up a cron job in Hermes to query the spend API and alert in Matrix when a key approaches its limit.
  • High availability — Current topology is single-replica SQLite. For HA, run N independent OmniRoute processes with separate data dirs (see OmniRoute docs on scale-out).

This post reflects a trimmed version of the stack running on my host as of November 2026. The deployed stack also includes an API gateway, per-profile secret-entry listeners, and a Gitea webhook relay. If you deploy this and hit something different, check the OmniRoute Docker Guide and Hermes Providers docs — both projects move fast.

Related