Ten fixes for running Hermes as an always-on Railway service with a Telegram bot

The author packaged Hermes in her own Docker image, deployed it to Railway as a persistent gateway, and connected Telegram, Notion MCP and fallback models. She wrote up the ten problems that cost her time. Most are general container lessons that apply to any Docker-based Hermes host.

Community reportRead the original (opens in a new tab)Source read 2026-10-20
Runs onRailway (custom Dockerfile on python:3.13-slim with Node and tini, persistent volume at /root/.hermes)
ModelsAnthropic Claude Sonnet 4.6 as primary; Nous Portal Hermes 4 405B and 70B as fallbacks
ChannelsTelegram
IntegrationsNotion MCP server (stdio, pre-installed via npm), Railway CLI, Telegram BotFather and userinfobot, Git hook for redeploys

Who built it

Tessa Kriesel, a developer relations leader who writes about developer tools. She wanted an agent she could message from her phone that didn't depend on her laptop being on, and she worked out the Railway deployment by trial, error and reading logs.

How it works

The repo holds a Dockerfile, an entrypoint.sh, a railway.json, and a hermes/ folder containing the config and a SOUL.md persona. The image installs Hermes from a pinned commit with --skip-setup, pre-installs the Notion MCP package, and uses tini as the init process. When the container starts, the entrypoint creates the Hermes directories and copies in the config and persona. It then writes secrets from Railway's environment variables into ~/.hermes/.env with restricted permissions and runs exec hermes gateway run.

Railway attaches a persistent volume at /root/.hermes, the whole Hermes home directory. The author learned this the hard way: when she mounted only the memories folder, every deploy wiped session history, cron jobs and the Telegram home channel. Hermes itself installs to /usr/local/lib/hermes-agent in the image, so the volume holds only data and config.

Telegram is set up with a BotFather token, the author's user ID in TELEGRAM_ALLOWED_USERS, and TELEGRAM_HOME_CHANNEL set to the same ID for a personal DM. Without a home channel the bot stayed silent and showed no error. The config uses the local terminal backend with a 180-second timeout, 60 max turns, medium reasoning effort and tool-loop guardrails.

For reliability, Claude Sonnet is the primary model, with two Nous Portal models as fallbacks. She lowered api_max_retries to 1 so a failing primary hands off quickly instead of retrying several times first. MCP servers go under the top-level mcp_servers: key; she reports that a nested mcp.servers key was silently ignored. Railway doesn't redeploy on push unless the repo is connected in its UI, so she triggers railway up --detach from a local git hook.

Her ten gotchas, in short: use gateway run, not gateway start, inside Docker. Mount the full Hermes home. Use the mcp_servers key. Make sure the gateway reads config.yaml (she copies the same file to both config.yaml and cli-config.yaml). Put secrets in ~/.hermes/.env. Set a home channel. Use tini so exited MCP subprocesses don't linger as zombies. Pre-install npx MCP packages to avoid cold-start failures. Cut retries when you have fallbacks. And don't worry that the volume hides the install.

Lessons you can use

  • Persist the entire Hermes home directory, not just memories. Sessions, cron jobs and gateway state live there too.
  • In a container, run the gateway in the foreground (hermes gateway run) under a proper init process, so restarts and MCP child processes behave.
  • Hermes reads secrets from its own .env file. If your platform injects environment variables, write them into that file at startup.
  • Pre-install stdio MCP servers in the image instead of letting npx download them on first use. Then ask the agent which tools it has to confirm they loaded.
  • If you configure fallback providers, lower the retry count so failover happens quickly, and double-check the fallback model names for your plan.
  • A silent bot often means setup isn't finished (here, no home channel), not a crash. Check the startup logs for the gateway banner.

Try a smaller version

  1. 1

    Before writing your own Dockerfile, check the official Hermes Docker image, which now runs gateway run under a supervisor and keeps all state in a single data volume.

  2. 2

    Create a Telegram bot, then set TELEGRAM_BOT_TOKEN, TELEGRAM_ALLOWED_USERS and either TELEGRAM_HOME_CHANNEL or /sethome in the chat.

  3. 3

    Mount a persistent volume at the Hermes home path, deploy, restart once, and confirm that a test memory and a test cron job survive the restart.

  4. 4

    Add one MCP server under mcp_servers: in config.yaml, with its secret in .env, and ask the agent to list its tools.

  5. 5

    Configure one fallback provider, set agent.api_max_retries low, and test failover by temporarily giving the primary a bad key.

Caveats

  • These gotchas reflect the Hermes version and docs at the time she wrote the post. Some may have changed since: the docs now cover Docker in detail, and newer official images use an s6 supervisor instead of tini.
  • Secrets end up in a plaintext .env file on the volume. Restricted permissions limit who can read it, but it isn't encrypted.
  • She reports no metrics. The custom git hook name and the unpinned MCP npm package are worth checking before anyone copies the setup exactly.

Original source

An original summary written from the public source on 2026-10-20. Figures and outcomes are the author’s own reports and were not independently verified.

Spotted something wrong or out of date? Tell us on X at @HermesWatcher (opens in a new tab) and we will fix it.