Try Hermes Agent, the self-improving AI agent from Nous Research, with your own Telegram bot and the model of your choice, in a few minutes. It runs shell commands, searches the web, remembers what you tell it and writes itself new skills as it works. You talk to it from Telegram (Discord and Slack work too).
It keeps what it learns. Deploying it adds a persistent volume, so its memory, the skills it learns, conversation history and files survive restarts and redeploys. Untick the volume when you deploy (or deploy on a plan without one) and it runs as a sandbox instead, starting from a clean slate on every restart: a good place to see what Hermes can do and hand it disposable tasks.
Deploy it on velixir, paste in a bot token, your Telegram user ID and a model key, and start chatting.
The template adds a volume and points HERMES_HOME at it (through VELIXIR_VOLUME_PATH), so
Hermes' whole state lives there and comes back after every restart. Snapshots are taken daily and
can be restored from the app's Volume tab.
Without a volume, every restart and every redeploy starts Hermes from a clean slate. What that means in practice:
| Without a volume: starts over on every restart | Survives every restart either way |
|---|---|
| Memory (what it has learned about you and your work) | Your settings: bot token, allowlist, model, keys |
| Skills it wrote for itself | The bundled skills that come with Hermes |
| Conversations and sessions | Which chat users may talk to it |
| Files it created, packages it installed, scheduled tasks |
Changing a variable on the Environment tab redeploys the app, so it resets then too. Within one run nothing is lost: the agent keeps its memory and skills across conversations until the next restart. The agent is told whether it has a persistent disk, so without one it will not promise to remember something forever.
- Hermes Agent v0.21.5, installed from the pinned upstream release with every dependency locked, including its terminal, file, web search, memory, skills and scheduling tools.
- A Telegram bot that is yours. Hermes connects to it and answers there; Discord and Slack can run alongside, or instead.
- Configured from the environment on every boot. No setup wizard and no shell needed: the template writes Hermes' configuration from your variables each time it starts.
- Strangers get nothing. Every chat platform needs an allowlist of user IDs. Messages from anyone else are dropped without a reply, and the template refuses to start with a wildcard or an allow-everyone switch.
- It asks before risky commands. Hermes sends deleting files and similar commands to your chat for a yes or no first. Unanswered after five minutes, they do not run.
- A status page at
/: whether Hermes is running, each chat platform's state, the model, how many messages from strangers it ignored, and the last problem with what to do about it. It never shows a token, a key or anyone's messages. - No crash loops. Missing settings get a setup page that names exactly what to set. If Hermes itself stops, it is restarted after a growing delay (5 seconds up to 5 minutes, or every 10 minutes for a problem a restart cannot fix, such as a rejected bot token), and the reason is on the status page.
-
Create a bot. In Telegram, open @BotFather, send
/newbotand choose a name. BotFather replies with a token like123456789:AAH.... -
Find your user ID. Message @userinfobot. It replies with your numeric user ID (a number, not your @name).
-
Pick a model provider from the table below and create an API key there.
-
Deploy this template with the button above, and set these on the app's Environment tab (mark the token and key as secret):
Variable Value TELEGRAM_BOT_TOKENThe token from BotFather TELEGRAM_ALLOWED_USERSYour user ID. Several people: IDs separated by commas OPENROUTER_API_KEY(or another provider's key)Your key HERMES_MODELOptional: the model to use, such as anthropic/claude-sonnet-5on OpenRouterSaving redeploys the app with the new settings.
-
Message your bot. Open the app's URL to see the status page. Hermes takes about 20 seconds to connect after each deploy; once it says Connected, say hello to your bot.
Until everything required is set, the app serves those steps as its home page instead of starting
an agent, and /healthz stays green.
| Provider | Key variable | Notes |
|---|---|---|
| OpenRouter | OPENROUTER_API_KEY |
One key for hundreds of models. Models look like anthropic/claude-sonnet-5. |
| Anthropic | ANTHROPIC_API_KEY |
Claude models, such as claude-sonnet-5. |
| OpenAI | OPENAI_API_KEY |
Models such as gpt-6-luna. |
| Nous Portal | NOUS_API_KEY |
An API key from the Portal. Also set HERMES_MODEL. |
| Anything OpenAI-compatible | HERMES_BASE_URL |
The API's base URL, such as https://api.mistral.ai/v1, with its key in HERMES_API_KEY and the model in HERMES_MODEL. |
- Your provider bills the tokens. Hermes sends a large system prompt and its tool list with every request, and it uses the same model for background jobs such as naming conversations and judging whether a command is risky, so an agent costs more per message than a plain chat. Set a spending limit with your provider.
- Leave
HERMES_MODELempty and Hermes picks its own default for that provider. Setting it is better: you know what you are paying for. Model names change often, so check your provider's list. - Several keys set? The first in the table wins. Set
HERMES_PROVIDER(openrouter,anthropic,openai,nousorcustom) to choose. - Other providers. Hermes supports many more natively (DeepSeek, Gemini, xAI and others). Set
HERMES_PROVIDERto its Hermes provider id and set the key variable named in Hermes' provider docs. - Nous Portal's browser sign-in does not fit a sandbox. Hermes' built-in Portal login keeps a token on disk, which is wiped on restart, so this template uses a Portal API key instead.
Every platform you turn on needs its own allowlist, and each works with or without Telegram.
- Discord: create an application in the
Developer Portal, copy the bot token from its Bot
page and turn on the Message Content intent there. Set
DISCORD_BOT_TOKENandDISCORD_ALLOWED_USERS(user IDs: turn on Developer Mode in Discord's Advanced settings, then right-click your name and choose Copy User ID). - Slack: Hermes uses Socket Mode, so it needs a bot token (
xoxb-) inSLACK_BOT_TOKENand an app-level token (xapp-) inSLACK_APP_TOKEN, plusSLACK_ALLOWED_USERS(member IDs, from your profile's three-dot menu).
Hermes' messaging docs have the step-by-step app setup for each.
Without a volume, everything resets on restart. A restart can also come from velixir itself (a redeploy, moving the app to another machine), not only from you. With the volume, its state stays; an app with a volume runs one replica and restarts on each deploy.
It needs an always-on plan. Hermes keeps a connection open to Telegram and hears messages through it, so it cannot sleep between them the way the Free plan does. Hermes uses about 200 MB when idle and 250 to 280 MB after some conversations, and a heavier task (cloning a repository and installing a package) took it to about 400 MB. Hobby's 512 MB is enough to try it and for light work. For bigger jobs, installing large packages or several people at once, choose Starter (1 GB): running out of memory restarts the app, which resets the sandbox.
It runs commands in its own container, as root. Its commands run inside this app, not on your computer, and a restart wipes whatever they changed. That container is also where Hermes itself runs: an experiment that installs conflicting Python packages or deletes the wrong files can break Hermes until the next restart. Restart the app and it is clean again.
Your keys live in that container too. Hermes keeps API keys out of the commands it runs and redacts them from its logs, but an agent that can run commands can, in principle, find them, for example if a web page it reads tricks it. Use a key with a spending limit, keep the allowlist to people you trust, and treat anything the bot says or does as coming from software acting on your behalf.
Messages from strangers are dropped silently. The status page counts them. If you messaged
the bot and got no answer, check your ID in TELEGRAM_ALLOWED_USERS: the app's logs show the
user ID of every message Hermes refused.
Groups need more setup. In a group, Telegram only shows a bot commands and replies unless you
turn privacy mode off in BotFather (/setprivacy). Hermes has its own group settings; see its
Telegram docs. One of them, TELEGRAM_GROUP_ALLOWED_CHATS, lets everyone in a listed group use
the agent, so list only groups whose members you would hand a shell.
Outbound traffic is web only. Apps on velixir can reach DNS and TCP ports 80 and 443. Model APIs and websites work; SSH to other machines, databases on other ports and email over SMTP do not, so Hermes' email platform will not connect. A self-hosted model must be reachable over HTTPS on 443.
No browser. The image leaves out Chromium to stay small, so Hermes' browser-control tools are
switched off. Web search and page reading still work, through Hermes' keyless free tiers or your
own search provider's key (for example FIRECRAWL_API_KEY or EXA_API_KEY).
Messages sent during a restart are skipped. Hermes drops what queued up while it was offline rather than answering stale requests. Send them again once the status page says Connected.
One copy at a time. Two copies of the app polling one bot token fight over it. A redeploy overlaps for a few seconds, and Hermes waits that out on its own.
On every start it downloads a small security scanner (tirith, from GitHub) that checks
commands before they run. If GitHub is unreachable, commands still run, without that check.
Upgrading happens here, not in the chat. Hermes' /update command cannot change this
install. See Upgrading Hermes below.
| Variable | Required | Meaning |
|---|---|---|
TELEGRAM_BOT_TOKEN |
for Telegram | The token from @BotFather. |
TELEGRAM_ALLOWED_USERS |
with it | Telegram user IDs allowed to use the agent, comma-separated. |
OPENROUTER_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, NOUS_API_KEY |
one model key | See Model providers. |
HERMES_BASE_URL, HERMES_API_KEY |
instead | Any OpenAI-compatible API and its key. |
HERMES_MODEL |
for Nous and custom | The model name. Optional for the others. |
HERMES_PROVIDER |
no | Which provider to use when several keys are set, or another Hermes provider id. |
DISCORD_BOT_TOKEN, DISCORD_ALLOWED_USERS |
for Discord | Bot token and user IDs. |
SLACK_BOT_TOKEN, SLACK_APP_TOKEN, SLACK_ALLOWED_USERS |
for Slack | Bot token, app-level token and member IDs. |
TELEGRAM_HOME_CHANNEL |
no | Where Hermes delivers scheduled tasks' results. Defaults to the first allowed user's chat. |
TELEGRAM_API_BASE |
no | A mock or self-hosted Bot API server, for testing. |
Other variables Hermes reads (search keys, HERMES_TIMEZONE, Telegram group settings) work as
its docs describe, except
that config.yaml is rewritten from the environment on every boot.
On Linux, macOS or WSL, with Python 3.11 to 3.13:
python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
python install_hermes.py # downloads and installs Hermes into .venv (a minute or two)
TELEGRAM_BOT_TOKEN=... TELEGRAM_ALLOWED_USERS=... OPENROUTER_API_KEY=... python start.pyThen open http://localhost:8080. Without the variables you get the setup page. Locally, Hermes'
state lives in ./.hermes and is not reset for you: delete that folder to start fresh. Use a
second bot for local runs, since two copies cannot share one token.
install_hermes.py pins one upstream release by tag and SHA-256. To move to a newer one, change
VERSION and SHA256 at its top (curl -sL <release tarball URL> | sha256sum gives the hash),
deploy, and read the release notes first:
the project moves quickly.
Upstream no longer distributes Hermes through PyPI, so the build downloads the pinned release from GitHub, checks its checksum and installs it with its locked dependencies, as upstream's own installer does.
start.py is the app's process. It checks the environment, writes Hermes' config.yaml, then
runs hermes gateway run as a child process and keeps it running. It serves the status page,
passes a redeploy's stop signal on to Hermes, and cleans up whatever the agent's commands leave
behind.
| Method | Path | Does |
|---|---|---|
GET |
/ |
The status page, or the setup steps when something required is missing |
GET |
/healthz |
200 while the app runs, setup mode included, with its state in the body |
Hermes Agent is open source under the MIT License, by Nous Research. This template's own code is MIT too.