By the end of this you'll have a Telegram bot that answers from your own Hermes Agent, replies only to people you've approved, and keeps running after you close the terminal. If Hermes already works on your machine, the Telegram part takes about ten minutes. The harder decision is where the gateway should live.
- Check that Hermes answers in the terminal with your model key before you touch Telegram.
- Create a bot with
/newbotin @BotFather and copy the token. - Get your numeric Telegram user ID from @userinfobot.
- Run
hermes gateway setup, pick Telegram, paste the token and your ID. - Keep the gateway running: a service on a server, a restart policy in a container.
- Message the bot, then send
/sethomeif you want scheduled jobs delivered there.
What you need before you start
You need a working Hermes install, a model key Hermes can already use, and a Telegram account. As of November 2026 the current release is v0.22.5, and every command below was checked against the official docs and the source code.
Test the model first. A bot that never answers looks exactly like a Telegram problem, even when the real fault is a mistyped API key or a model name your provider doesn't recognise. Rule that out before you create anything:
hermes setup # guided: provider, API key, model
hermes # ask it something simple and wait for an answer
Keys end up in ~/.hermes/.env (a DeepSeek key, for example, is stored as DEEPSEEK_API_KEY), and the model choice goes into config.yaml. If Hermes answers here, everything after this point is plumbing. Still choosing a model? See what an agent costs per month in tokens. The last requirement is easy to miss: a machine that stays on.
How Hermes talks to Telegram
Telegram never runs your agent. Your bot is an account on Telegram's servers, identified by a token. The Hermes gateway is a separate, long-running process on your machine. It asks Telegram for new messages, checks who sent each one, hands approved messages to the agent, and posts the reply back through the Bot API.
Two consequences follow. You don't need a domain or an open port, because long polling (the default) is outbound only. And the bot answers only while the gateway runs. If it stops, messages wait on Telegram's side, and on a cold start Hermes drops that backlog by default rather than answering hours-old requests (drop_pending_on_cold_boot changes that).
Create the bot with @BotFather
BotFather is Telegram's own bot for creating and managing bots. Search for @BotFather in Telegram (or open t.me/BotFather) and follow the steps in Telegram's BotFather docs:
- Send
/newbot. - Give it a display name. This is what people see in chats, and you can change it any time.
- Pick a username. It has to end in
bot, be 5 to 32 characters, and use only Latin letters, digits and underscores. You can't rename it later, so skip the joke name. - Copy the token BotFather sends back. It looks like
110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw(Telegram's own example). Whoever holds it controls the bot, so keep it in a password manager.
There's also a shortcut. Recent versions of hermes gateway setup offer an Automatic option for Telegram: scan a QR code, confirm in Telegram, and Hermes creates the bot through a Nous Research onboarding service and saves the token locally. The dashboard has it too, as a "Create with QR" button on the Messaging, Telegram page. This guide follows the manual route because it works on every version and you know exactly where the token came from.
Get your Telegram user ID
Hermes identifies people by their numeric Telegram user ID, not their @username. Usernames can be changed or dropped; the number stays with the account. To get yours, message @userinfobot or @get_id_bot (both suggested in the Hermes docs). Either replies with a number like 123456789. Save it.
Why the allowlist matters
Your bot's username is public. Anyone can find it and message it, and depending on your configuration, the agent behind it can run shell commands on the host and spend your model budget. TELEGRAM_ALLOWED_USERS keeps that limited to you.
Hermes is strict by default. With no allowlist and no other access rule, the gateway denies everyone. Once you set an allowlist, strangers who DM the bot get silence, and the gateway logs a warning with their numeric ID. If you choose DM pairing instead, they get a one-time code that does nothing until you approve it on the server. Either way, the agent never sees their message.
The way to undo all that is the wizard's open-access option. Leave the allowed-users prompt empty and the wizard asks how to handle unknown users; the first choice writes GATEWAY_ALLOW_ALL_USERS=true. Don't pick it. The Hermes security docs say never to use it in production, and a bot with tool access is production the moment it's live.
hermes gateway setup vs editing the .env file
Use the wizard the first time:
hermes gateway setup
Pick Telegram from the platform list. Current versions then ask whether to create the bot automatically or paste a BotFather token; choose manual and paste it. Next comes the allowed-users prompt: enter your ID, plus any others separated by commas. The wizard then offers to use your ID as the home channel (sensible for a personal bot) and to install, start or restart the gateway service.
Editing .env by hand
Everything the wizard collects lands in one plain-text file: ~/.hermes/.env, or $HERMES_HOME/.env if you've moved Hermes's home. In the official Docker image the home is /opt/data, so the file is /opt/data/.env. The two lines that matter:
# ~/.hermes/.env
TELEGRAM_BOT_TOKEN=123456789:replace-with-the-token-from-BotFather
TELEGRAM_ALLOWED_USERS=123456789,987654321
Hand-editing suits scripted installs and adding a teammate's ID. Keep the IDs comma-separated with no spaces, run chmod 600 on the file (the security docs recommend it), and restart the gateway afterwards with hermes gateway restart, because a running gateway won't notice the edit. hermes config set also writes UPPER_SNAKE names to .env. That's fine for IDs, but it leaves a token sitting in your shell history, so paste tokens through the wizard or an editor.
Start the gateway and keep it running
Run it in the foreground first so you can watch it connect:
hermes gateway
Send your bot a message from Telegram. Per the Hermes Telegram docs it should come online within seconds. Press Ctrl+C once you've seen a reply, then make it permanent:
hermes gateway install # user service on Linux, launchd agent on macOS
hermes gateway status
hermes gateway restart # lets in-flight replies finish first
On Windows, install uses Task Scheduler, and the gateway docs warn its restart policy covers only the launcher: a gateway that crashes later stays down until you run hermes gateway start.
What "always-on" means on a laptop, a server and a container
| Where it runs | When the bot answers | How to keep the gateway up |
|---|---|---|
| Laptop | Only while it's awake and online | hermes gateway install (user service or launchd) |
| VPS or home server | Around the clock, through logouts and reboots | sudo hermes gateway install --system, or a user service plus linger |
| Docker container | As long as the container runs | gateway run as the main process with --restart unless-stopped |
| Windows | Until the gateway crashes | Task Scheduler; check hermes gateway status after problems |
A laptop is fine for trying this out, but the bot goes quiet when the lid closes. For anything other people rely on, use a machine that doesn't sleep; the team assistant guide says a small VPS is enough.
On a Linux server, a system service starts at boot with no login session. A user service stops when you log out of SSH unless you enable linger:
# option 1: boot-time system service
sudo hermes gateway install --system
journalctl -u hermes-gateway -f
# option 2: user service that survives logout
hermes gateway install
sudo loginctl enable-linger $USER
journalctl --user -u hermes-gateway -f
Containers are different. There's no systemd inside, and hermes gateway install refuses to install a user service there. The Docker docs run the gateway as the container's main process and let the runtime restart it:
docker run -d --name hermes \
--restart unless-stopped \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent gateway run
The official image also supervises the gateway with s6 and restarts it after a crash. Keep /opt/data on a volume, because that's where .env, sessions, memories and skills live, and it's the part that survives an image upgrade. We go into that in updating Hermes without losing data.
Set a home channel with /sethome
The home channel is where Hermes delivers messages nobody asked for in the moment, such as cron results and other proactive messages. If you'll schedule anything, set one: open the chat you want (your DM with the bot, or a group it's in) and send /sethome. Or put it in .env:
TELEGRAM_HOME_CHANNEL=-1001234567890
TELEGRAM_HOME_CHANNEL_NAME="Team Updates"
Your DM chat ID is the same number as your user ID. Group IDs are negative, like the one above; add @userinfobot to the group to read it. Then ask for a schedule in plain language, like "every weekday at 8:00, check disk space on this server and report anything over 80% full", and /cron list shows the job. One trap from the docs: scheduled prompts run in a fresh session with no memory of earlier chats, so write each one as if for a stranger.
Why the bot doesn't reply in groups
When a bot works in DMs and goes silent in a group, the cause is usually on Telegram's side. Bots run in privacy mode by default: in groups they receive commands and replies to their own messages, but not ordinary chat. So you add the bot, type "summarise this thread", and nothing happens.
There are two fixes. Turn privacy off in BotFather (/mybots, choose the bot, then Bot Settings, Group Privacy, Turn off), or make the bot an admin of the group, since admin bots receive every message. Then remove the bot from the group and add it back. Telegram applies a privacy change only when the bot rejoins, and skipping that step makes the fix look broken.
Then the Hermes side. The allowlist applies in groups too, so a teammate who isn't on TELEGRAM_ALLOWED_USERS is ignored even when Telegram delivers their message. TELEGRAM_GROUP_ALLOWED_CHATS authorizes every member of the listed groups instead; use it only where you'd trust every member with the agent's tools.
Last, decide whether the bot should speak only when spoken to. With privacy off and no mention rule, Hermes answers every message it can see, which gets old fast. In anything bigger than a two-person chat, add:
TELEGRAM_REQUIRE_MENTION=true
TELEGRAM_GROUP_ALLOWED_CHATS=-1001234567890 # only if the whole group should have access
Now it answers @mentions, replies to its own messages and commands like /new@your_bot, and stays out of everything else.
Share one bot with a team
The simple way to add people is a longer allowlist: TELEGRAM_ALLOWED_USERS=123456789,987654321, then restart the gateway. You don't even need to ask for IDs: when someone not on the list messages the bot, the gateway logs a warning with their numeric ID and posts a one-time note to your home channel, if you have one.
The other way is DM pairing. A new person messages the bot, gets a one-time code, sends it to you, and you approve it on the server:
hermes pairing approve telegram XKGH5N7P
hermes pairing list
hermes pairing revoke telegram 123456789
Approval works immediately, with no restart. Codes expire after an hour, and five failed approval attempts lock pairing for that platform for an hour. Pairing codes go out automatically only when no allowlist is set; with an allowlist, Hermes stays silent toward strangers unless you switch Telegram's unauthorized_dm_behavior to pair in config.yaml. In Docker, run the pairing commands as the hermes user (docker exec -u hermes …). The security docs warn that approvals written as root can be silently ignored.
Two more things. Everyone you allow gets the same powers unless you split admins from users with allow_admin_from and user_allowed_commands. And one token belongs to one running gateway: Telegram rejects two processes polling the same token, so testing on your laptop while the server is live makes replies go missing. Make a second bot for testing.
Give it a personality with SOUL.md
~/.hermes/SOUL.md (or $HERMES_HOME/SOUL.md) is the first thing in Hermes's system prompt. It sets tone and habits, not project facts; those belong in AGENTS.md. Keep it short. A team bot might use something like:
You are the operations assistant for a small web team.
Keep replies short. Put commands and config in code blocks.
If a request is ambiguous, ask one question before acting.
Never repeat API keys, tokens or passwords back to anyone.
For a one-off change of tone in a chat, built-in presets work from Telegram too: /personality concise, and /personality none to clear it.
Troubleshooting: Hermes Agent Telegram bot not responding
Check the gateway first, then the allowlist, then Telegram's side.
| What you see | Likely cause | What to do |
|---|---|---|
| Bot silent everywhere, even in DMs | Gateway not running, or running on a different machine | hermes gateway status; read the gateway log; start it |
| Silent to you, and the log warns about a sender it dropped | Your ID isn't on the allowlist (typo, or you entered your @username) | Copy the numeric ID from the log into TELEGRAM_ALLOWED_USERS, restart |
| Bot replies with a pairing code | No allowlist; pairing mode is handling unknown users | hermes pairing approve telegram CODE, or add your ID |
| Stopped working after you revoked or regenerated the token | The old token is still in .env | Copy the current token from BotFather into .env, restart |
| Works in DMs, silent in a group | Privacy mode, not re-added, sender not allowed, or mention filter | Go through the four gates above in order |
| Replies come and go | Two gateways polling the same token | Stop one, or give each its own bot |
| Bot answers with errors | Model key or model name problem | Test with hermes in the terminal; run hermes doctor |
| Webhook mode, no replies | URL not reachable over HTTPS, or missing secret | Check TELEGRAM_WEBHOOK_URL, the firewall, and TELEGRAM_WEBHOOK_SECRET |
You almost certainly don't need webhooks. Long polling works behind NAT with no open ports; webhook mode exists for cloud platforms that put idle machines to sleep. If you do set TELEGRAM_WEBHOOK_URL, the gateway refuses to start without TELEGRAM_WEBHOOK_SECRET, and the docs suggest generating it with openssl rand -hex 32.
Logs live in journalctl on Linux services and in ~/.hermes/logs/gateway.log elsewhere. Read them before changing anything; the allowlist warning alone saves a lot of guessing.
Keep the token and your keys out of chats
The bot token is a password for the bot. Keep it out of git, screenshots and support tickets. If it leaks, send /revoke to BotFather, put the new token in .env and restart the gateway.
The same goes for model keys. We recently had a buyer ask for help setting up Hermes with DeepSeek and Telegram, and they pasted their DeepSeek API key straight into the marketplace chat while asking. Nobody helping you needs the key itself. It goes into .env on the machine, through hermes setup or hermes model. Treat a key that's been pasted into any chat as public: revoke it at the provider and make a new one.
That includes the bot's own chat: a key typed to your bot sits in your Telegram history and in Hermes's session files under ~/.hermes/sessions/.
Before you share the bot: allowlist set, no GATEWAY_ALLOW_ALL_USERS or TELEGRAM_ALLOW_ALL_USERS, .env at chmod 600, and the gateway running as a normal user, not root. The Hermes docs also recommend a sandboxed terminal backend, such as Docker, for bots that run commands. Our security checklist for self-hosted agents covers the rest.
Or let us host it
If you'd rather not keep a server awake for this, HostAiHub runs Hermes for you. Each agent gets its own private container on a private SSL subdomain (name.ai.hostaihub.com), deployed automatically in a few minutes, with daily backups kept for 7 days and health checks that restart it automatically. You still bring your own LLM key. On HostAiHub every Hermes agent comes with a "Setup guide & terminal" page in the panel where you run hermes gateway setup.
Hermes plans are $10 a month, $100 a year or $250 lifetime; see pricing. If you don't have a model key yet, our OpenAI-compatible AI API works with prepaid credit, and DeepSeek-V4-Flash costs $0.028 per million input tokens and $0.056 per million output tokens on the models page.
Questions people ask
Do I need a server or a public URL to use Hermes with Telegram?
You don't need a public URL or an open port, because the Hermes gateway uses long polling by default and only makes outgoing connections. You do need the gateway running somewhere: a laptop works while it's awake, while a server or container keeps the bot answering around the clock.
How do I find my Telegram user ID for TELEGRAM_ALLOWED_USERS?
Message @userinfobot or @get_id_bot in Telegram and it replies with your numeric ID, such as 123456789. Use that number, not your @username, in TELEGRAM_ALLOWED_USERS.
Why is my Hermes Telegram bot not responding?
The usual causes are a gateway that isn't running, a user ID missing from TELEGRAM_ALLOWED_USERS, or a wrong token. Run hermes gateway status, then read the gateway log, which names any sender it rejected.
Why doesn't my Hermes bot reply in a Telegram group?
Telegram bots run in privacy mode by default and don't see normal group messages. Turn privacy off in BotFather or make the bot an admin, remove and re-add it to the group, and make sure the sender is on the allowlist or the group is allowed.
Can several people use the same Hermes Telegram bot?
Yes. Add their numeric IDs to TELEGRAM_ALLOWED_USERS separated by commas and restart the gateway, or, if you use DM pairing, approve each person's code with hermes pairing approve telegram. Everyone allowed gets full access unless you set allow_admin_from.
What does /sethome do in Hermes?
It makes the current chat the home channel, where Hermes delivers cron job results and other proactive messages. You can also set it with TELEGRAM_HOME_CHANNEL in the .env file.
Can I run the same bot token on my laptop and my server?
No. Telegram rejects two processes polling the same token, so replies go missing. Create a separate bot in BotFather for testing.