| Runs on | Railway (custom Dockerfile on python:3.13-slim with Node and tini, persistent volume at /root/.hermes) |
|---|---|
| Models | Anthropic Claude Sonnet 4.6 as primary; Nous Portal Hermes 4 405B and 70B as fallbacks |
| Channels | Telegram |
| Integrations | Notion 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
.envfile. 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
Before writing your own Dockerfile, check the official Hermes Docker image, which now runs
gateway rununder a supervisor and keeps all state in a single data volume. - 2
Create a Telegram bot, then set
TELEGRAM_BOT_TOKEN,TELEGRAM_ALLOWED_USERSand eitherTELEGRAM_HOME_CHANNELor/sethomein the chat. - 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
Add one MCP server under
mcp_servers:inconfig.yaml, with its secret in.env, and ask the agent to list its tools. - 5
Configure one fallback provider, set
agent.api_max_retrieslow, 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
.envfile 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
- How I Deployed Hermes Agent on Railway with Telegram (And Every Gotcha I Hit Along the Way) (opens in a new tab)
- Community library entry
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.