I like the idea of messaging an agent from my phone, letting it do a job, and getting the result back without leaving a laptop open. I’m less enthusiastic about building all of my routines and working context inside a service I can’t move away from.
Hermes Agent, from Nous Research, is an interesting option here. You run the agent, choose its model provider and connect a messaging interface. Telegram is convenient for the last part: I already know how to send a message, upload a document and read a reply.
This guide sets up Hermes on a Linux server with DeepSeek V4.1 Flash Fast through Command Code’s API and a private Telegram bot. It is a documentation-based walkthrough, not a report of a deployment I’ve benchmarked. The commands and provider names were checked against the linked documentation on 30 September 2026. Model catalogues and installer behaviour can change; check them before spending money or granting access.
What this replaces, and what it doesn’t
Meta’s Muse, xAI’s Grok Bot and ChatGPT Dots offer versions of the persistent personal-agent idea. Grok Bot and Dots include cloud computers and integrated ways to work across apps. The appeal is that someone else assembles and operates the service.
Hermes can be an alternative for specific jobs: process a file, work in a dedicated workspace, reuse a procedure or send the result of a scheduled task. Installing it does not recreate every browser integration, voice feature, approval system or collaborative workflow in those products. Start with the jobs you actually need.
You also take over maintenance. If the bot stops replying, there is no hosted product team watching your particular process. You need to inspect logs, manage credentials, update the software and restore it when the server fails.
What runs where
There are three separate components:
| Component | Location | Responsibility |
|---|---|---|
| Telegram | Telegram’s infrastructure and your devices | Messages and attachments |
| Hermes gateway and agent | Your Linux server | Sessions, tools, saved state and job execution |
| DeepSeek inference | Your chosen API provider | Model responses and tool-call decisions |
Self-hosting Hermes is not self-hosting DeepSeek’s weights. This setup needs no inference GPU on your server, but sends model context to a remote provider. Changing the model endpoint later doesn’t require replacing the Telegram bot.
Telegram bot conversations are cloud chats, not end-to-end encrypted Secret Chats. Your server, model provider and any external tools add their own data-handling boundaries. Don’t put passwords, recovery codes or private customer records into this setup just because you own the VPS. See Telegram’s privacy explanation.
1. Use a dedicated Linux account
For this walkthrough, use an Ubuntu server with systemd and SSH access. My starting allocation would be 2 vCPUs, 4 GB RAM and enough disk for the tools and documents you intend to keep. That is a planning suggestion, not a measured Hermes minimum. Browsers, containers and local transcription can need more.
Keep the server separate from production databases and personal files. Restrict inbound access to SSH from your own network or VPN. Telegram polling needs outbound connectivity, so there is no reason to expose a public agent API for this setup.
From an existing administrator account:
sudo apt update
sudo apt install -y git curl tar ca-certificates build-essential
sudo adduser hermes
sudo loginctl enable-linger hermes
sudo -iu hermes
Do not add the new account to the sudo group. The administrator installs system dependencies; the agent account runs Hermes. A process able to execute shell commands deserves a smaller permission boundary than your everyday admin account.
2. Install Hermes as that user
The official source installer is a shell script. I prefer to download it first, inspect it and then run it:
curl -fsSL https://hermes-agent.nousresearch.com/install.sh \
-o /tmp/hermes-install.sh
less /tmp/hermes-install.sh
bash /tmp/hermes-install.sh
export PATH="$HOME/.local/bin:$PATH"
hermes doctor
Inspecting a script is not a guarantee of safety. You are trusting the installer and the dependencies it retrieves. Use the official domain, read its output, and fix prerequisite failures before continuing.
The installer manages its runtime dependencies. Don’t copy an old tutorial’s Python environment commands over the top of it. See the current installation instructions for platform-specific requirements.
3. Get the model name right
“Flash” and “Flash Fast” are not interchangeable API strings. The relevant routes at the time of writing are:
| Route | API base URL | Model ID |
|---|---|---|
| Command Code Flash Fast | https://api.commandcode.ai/provider/v1 | deepseek/deepseek-v4.1-flash-fast |
| Direct DeepSeek Flash | https://api.deepseek.com | deepseek-flash |
| OpenRouter Flash | https://openrouter.ai/api/v1 | deepseek/deepseek-v4.1-flash |
Command Code describes its Flash Fast offering as a high-throughput V4.1 Flash route. DeepSeek’s own API documentation uses deepseek-flash for V4.1 Flash. Don’t send the Command Code identifier to DeepSeek’s endpoint.
For the main route, create a Command Code API key in Studio and confirm your plan permits API use. Its Provider API documentation says the Go plan does not; the other listed plans have API access.
Check the current catalogue before configuring Hermes:
curl -fsSL https://api.commandcode.ai/provider/v1/models \
| less
Look for the exact model ID and its supported endpoints. If it isn’t present, stop and check the provider’s current catalogue. Don’t silently replace it with an older Flash route.
Now run:
hermes model
Choose the custom OpenAI-compatible endpoint option, then enter the Command Code base URL, your API key and the exact Flash Fast model ID above. Menu wording may vary by release. Use the wizard’s credential prompt rather than putting the key into a shell command or a screenshot.
Hermes supports this custom endpoint path, as documented in its provider guide. The non-secret portion of the resulting ~/.hermes/config.yaml should identify the route:
model:
provider: custom
base_url: https://api.commandcode.ai/provider/v1
default: deepseek/deepseek-v4.1-flash-fast
That block is an inspection example, not a complete replacement for the wizard’s configuration. Preserve its credential settings and other keys. If you edit YAML, update existing blocks rather than adding duplicate model: entries.
Run hermes and ask a simple question. Check the active model with /model, then quit before setting up the gateway. Establish that the model connection works independently of Telegram.
Other routes, if you want them
For direct DeepSeek, rerun hermes model with the direct endpoint and deepseek-flash. This is a different route, with DeepSeek’s own billing and service behaviour.
OpenRouter also lists V4.1 Flash. Its :nitro suffix favours throughput and makes priority-tier endpoints eligible, so deepseek/deepseek-v4.1-flash:nitro is a speed-oriented alternative there. It is not a promise of the same backend as Command Code Flash Fast, nor necessarily the cheapest option.
A fast route also doesn’t guarantee a fast completed job. Tool latency, context size and the number of model calls still count. Compare the time and cost of a whole task before deciding which route to keep.
4. Set the execution boundary before connecting chat
Run hermes tools and enable only what you need. Start without email sending, production credentials or access to your whole home directory.
For shell work, I’d use the Docker terminal backend on this dedicated machine. Have the administrator install Docker using its Ubuntu instructions or set up rootless Docker. Don’t casually add the agent account to the rootful Docker group: Docker documents that this grants root-level privileges.
Once Docker works from the hermes account, test it there:
docker info
docker run --rm hello-world
hermes config set terminal.backend docker
hermes config set terminal.docker_mount_cwd_to_workspace false
The last setting avoids automatically mounting the gateway’s working directory. Start with no custom host mounts or forwarded credentials. Ask the agent to run pwd and create a disposable text file, then confirm its commands really run inside the container. See Hermes’s terminal configuration.
An important detail: Hermes’s security guide says dangerous-command approval checks are skipped for Docker-backed terminal execution. The container is treated as the boundary. Do not assume you’ll receive an approval prompt before a destructive command inside it.
A writable host mount or forwarded token gives commands access to that resource. Containers also don’t prevent an agent from sending accessible information over the network. Avoid mounting the Docker socket, SSH directory, provider credentials or unrelated projects. For external actions, restrict the actual tool permissions and credentials; a prompt saying “ask first” is useful guidance, not an enforcement mechanism.
5. Create your Telegram bot
Open the official BotFather, send /newbot, and choose a name and a unique username ending in bot. Keep the returned token secret. If it leaks, revoke it through BotFather.
Find your numeric Telegram user ID. Hermes’s guide suggests @userinfobot; remember this is another bot that receives your message. Your @username is not the numeric ID used by Hermes’s allowlist.
On the server:
hermes gateway setup
Choose Telegram and enter the token and your numeric user ID. Use only your ID initially. The Telegram setup guide documents these settings in ~/.hermes/.env:
TELEGRAM_BOT_TOKEN=REPLACE_WITH_YOUR_BOT_TOKEN
TELEGRAM_ALLOWED_USERS=REPLACE_WITH_YOUR_NUMERIC_USER_ID
These are placeholders, not usable credentials. Let the wizard write the real values. Restrict the credential file:
chmod 600 ~/.hermes/.env
Keep this bot in a private DM. There is no need to disable BotFather’s group privacy mode for a DM. If you later add it to a group, revisit exactly which messages it receives and who may trigger it. Telegram explains bot visibility and privacy mode.
6. Test in the foreground, then install the service
Start the gateway manually:
hermes gateway
Open the bot in Telegram, press Start and send a plain text question. Then ask it to create a harmless text file in its sandbox. If available, use a second Telegram account to confirm it cannot start an agent task. An allowlist is something to test, not merely something to type into a file.
Stop the foreground process with Ctrl-C before starting a supervised one. Two gateways polling the same token can conflict.
As the hermes user:
hermes gateway install
hermes gateway start
hermes gateway status
journalctl --user -u hermes-gateway -n 50 --no-pager
We enabled lingering earlier so the user service can survive logout and start at boot. This avoids giving the agent account sudo for routine restarts. Hermes documents user and system service options in its gateway guide.
Log out, message the bot again, then test once more after a planned reboot. A running process in an SSH session is not the same as a working unattended service.
This setup uses Telegram’s default long polling. It doesn’t need a domain, HTTPS certificate, Cloudflare Worker or inbound webhook port. Webhooks are an alternative for hosts designed to wake on inbound requests, not a prerequisite for an always-on VPS.
7. Give it a small first job
I’d begin with a document task, not my inbox:
Read the attached release notes. List changes affecting an Astro site,
link each item to its source, and separate confirmed changes from guesses.
Return a Markdown checklist. Do not change files or install packages.
Check the references yourself. Then try a task that creates an output file. With Docker, the gateway sends attachments from the host, so a file path that exists only inside the container won’t be enough. If you need this workflow, add one dedicated output-directory mount and use the host-visible path described in the Telegram file-delivery instructions. Don’t solve a missing attachment by mounting your entire home directory.
Once manual work is useful, try one scheduled task. Set the destination with /sethome in your private chat. Specify the timezone, source, output and end date, then ask it to confirm what was scheduled. Use /cron list to inspect jobs. Hermes’s Telegram assistant tutorial notes that cron prompts need their own context; don’t rely on yesterday’s conversation to supply missing URLs or paths.
For example: check one release feed each weekday, send an update only when there is a relevant release, and stop after two weeks. A useful first automation has a clear reason to stay quiet.
8. Keep a lid on spend
An agent may make many model calls for one message. Each can carry conversation history and tool output. Compare total usage for the completed job, not just the price of its final reply.
Check the Flash Fast route’s current rates, peak pricing and plan limits on the provider’s pages. A discount for ordinary Flash may not apply to Flash Fast. API use through Hermes also does not inherit every efficiency of Command Code’s own coding harness.
For initial experiments, set a small iteration cap:
hermes config set agent.max_turns 20
Hermes documents this in its iteration-budget settings. It limits iterations per conversation turn, not monthly dollars, token volume or the number of scheduled runs. Use provider-side spending controls where available, keep the starting balance modest, and inspect usage after the first few jobs. Avoid automatic top-ups until you understand the workload.
Account for the VPS, inference, backups and any extra search, browser or speech services. Don’t assume that choosing Flash Fast configures every auxiliary model or tool. Review those routes before enabling more capabilities.
9. Back up before updating
For a source installation:
hermes backup
hermes update --check
hermes update --backup
hermes gateway restart
hermes gateway status
Use the update instructions appropriate to your install method. A package or container image may have a different update owner.
Full backups include sensitive configuration and credentials. Encrypt them, store a copy off the VPS and never upload one to a public issue. The CLI backup reference also lists exclusions: a backup is not a complete application image or a copy of every browser login.
Test restoration on a separate machine. Install Hermes, import the backup with hermes import /path/to/backup.zip, inspect paths and credentials, and run diagnostics. Keep the restored gateway stopped until you are ready to switch over; otherwise both machines may execute scheduled jobs or poll the same Telegram bot.
When it doesn’t reply
Start on the server, with hermes gateway status, hermes doctor and the user-service journal. Use the Hermes troubleshooting reference alongside the provider’s error response.
| Symptom | Check first |
|---|---|
| CLI fails too | Model ID, endpoint, key, balance and API entitlement |
| CLI works, Telegram doesn’t | Gateway logs, bot token and numeric user allowlist |
| Polling conflict | A second gateway on this machine or another host; an existing webhook |
| Stops after SSH logout | User service installation and lingering for the correct account |
| Reply works, attachment fails | Whether the gateway can read the reported host file path |
| Shell task fails | Docker availability to the service user, mounts and container dependencies |
| Model loops or spends too much | Tool failures, repeated context, iteration cap and provider usage |
Provider errors deserve their own interpretation. A 401 calls for checking credentials; an access or billing error won’t be repaired by repeated retries. For rate limits or an outage, wait and inspect the provider status before changing unrelated parts of the setup.
Keep diagnostic output private until you’ve checked it for tokens, file contents and conversation excerpts. “Share the logs” is not permission to publish everything the agent has seen.
The part worth owning
I don’t want to administer a server merely to get another chat window. The setup is worthwhile if it does a few recurring jobs well and I can keep the useful pieces when my model preferences change.
Start with one private bot, one model route and one narrow job. Keep the workspace inspectable, test the restrictions, and make sure you can restore it. Add more access only when the task requires it.
The benefit is having a practical exit: my notes, configuration and routines can move, and the model endpoint can change. There is still work involved in keeping that exit usable.