Self-host Hermes Agent
An agent that remembers you, on a computer that never sleeps.
Shelley handles the setup. About 10 minutes.
Prefer a terminal?
curl -fsSL https://exe-staging.dev/deploy/hermes-agent/prompt.txt | ssh exe.dev new --prompt=/dev/stdinHermes Agent, from Nous Research, is an open-source agent that remembers what you tell it, writes itself new skills as it works, and runs jobs on a schedule. It can run commands, browse the web, and read and write files. Give it a computer of its own, one that stays on, and talk to it from your browser or your phone.
Model usage is billed to your exe.dev account through the LLM integration. There's no API key to find.
What you get#
- A private dashboard at
https://<vm>.exe.xyz/with browser chat, behind your exe.dev login and a generated Hermes password. - Claude through exe.dev's LLM integration, with no API key to paste, and GPT and open models a
/modelaway. - A gateway and dashboard that stay on when you disconnect and start again after a reboot, so scheduled jobs keep running.
- Nightly backups of memory, skills, sessions, and settings in
~/backups/hermes. - If you gave a bot token, a Telegram bot that answers only your account.
What's running, and where
https://my-hermes-agent.exe.xyz/
The Hermes dashboard, behind your exe.dev login and a Hermes password
Deploy with Shelley#
Click Deploy and Shelley will set up Hermes Agent on a new VM, then check that it works. No config-file wrangling required.
Have these ready; Shelley asks for them:
TELEGRAM_BOT_TOKEN: optional; from @BotFather's/newbotin Telegram. Leave it as is to skip TelegramTELEGRAM_USER_ID: optional; your numeric Telegram user ID (message @userinfobot), so the bot answers only you
Prefer a terminal?
curl -fsSL \
https://exe-staging.dev/deploy/hermes-agent/prompt.txt \
| ssh exe.dev new --prompt=/dev/stdinWhat Shelley is told
TELEGRAM_BOT_TOKEN=<fill-this-in>
TELEGRAM_USER_ID=<fill-this-in>
Set up Hermes Agent on this VM by following the exe.dev recipe at https://exe-staging.dev/deploy/hermes-agent.md from top to bottom.
The values at the top of this message are mine:
- TELEGRAM_BOT_TOKEN: optional; from @BotFather's `/newbot` in Telegram. Leave it as is to skip Telegram
- TELEGRAM_USER_ID: optional; your numeric Telegram user ID (message @userinfobot), so the bot answers only you
Ask me for any that still say <fill-this-in> before you need them.
The recipe's example VM name is my-hermes-agent. This VM's name is the output of `hostname`: use it wherever the recipe writes my-hermes-agent. Skip the step that creates the VM; you are on it.
You are done when all of these are true. Check each one yourself:
- A private dashboard at `https://<vm>.exe.xyz/` with browser chat, behind your exe.dev login and a generated Hermes password.
- Claude through exe.dev's LLM integration, with no API key to paste, and GPT and open models a `/model` away.
- A gateway and dashboard that stay on when you disconnect and start again after a reboot, so scheduled jobs keep running.
- Nightly backups of memory, skills, sessions, and settings in `~/backups/hermes`.
- If you gave a bot token, a Telegram bot that answers only your account.
Read the whole recipe, including the closed details. For initial setup,
follow "Install by hand" first, skipping VM creation and SSH. The
opening walkthrough is for the owner. Do not run the restore or update
instructions.
Telegram is optional. If TELEGRAM_BOT_TOKEN is empty or still says
<fill-this-in>, skip Telegram and don't ask for it. Otherwise follow
"Connect Telegram by hand" with the supplied token and user ID instead
of interactive reads. Ask for the user ID only if a token was given.
If the gateway log says the token was rejected, unset both Telegram
settings, restart the gateway, and tell the owner. Never echo the bot
token back.
Keep the VM private. Never run share set-public, bind the dashboard to a
non-loopback address, or remove its password. On an existing VM, ask
before replacing configuration or a service on port 8000.
Verify a model reply, both services enabled and active, Linger=yes,
`auth_required` true with `basic` through nginx, the backup timer, and a
tested archive. The Chat page is a terminal over a WebSocket: a sign-in
through nginx is check enough; don't script typing into it. Do not
reboot.
Finish with the dashboard URL and the Hermes username and password from
"Open the dashboard"; this conversation is private to the owner.
Finish with the URLs I should open.
Also at /deploy/hermes-agent/prompt.txt. The recipe it follows is /deploy/hermes-agent.md.
Say hello#
Open https://my-hermes-agent.exe.xyz/ and sign in to exe.dev. Then sign
in to Hermes with the username and password from Shelley's last message,
and click Chat. Try:
Remember that I drink my coffee black and I'm learning Portuguese. Then give me three five-minute practice ideas.
Start a New chat and ask what it knows about you. It remembers across conversations, and gets better at the things you ask for often. (Hermes also asks, once, whether to share usage data with Nous Research. Your call.)
Lost the password? On your computer:
ssh my-hermes-agent.exe.xyz "grep -E '^HERMES_DASHBOARD_BASIC_AUTH_(USERNAME|PASSWORD)=' ~/.hermes/.env"
Warning
Keep this VM private. Hermes runs commands on the VM, with sudo. It asks before risky ones; leave that on. Don't make the dashboard public or keep unrelated secrets on this VM, and keep bot tokens to yourself.
Put it on a schedule#
The VM keeps UTC time, so first give Hermes yours. Ask Shelley, or on the VM (with your own time zone):
hermes config set timezone Europe/Lisbon
systemctl --user restart hermes-gateway hermes-dashboard
Reload the dashboard, and ask for something regular:
Every weekday at 8:00, send me the five top Hacker News stories, one line each.
Jobs run inside the gateway, so they carry on while your laptop sleeps. Pause or delete them on the dashboard's Cron page. Results of jobs set up in the browser wait under Sessions, on the Automation tab; jobs you ask for from Telegram report back there.
Take it to Telegram#
Happy in the browser? Skip this bit.
The quickest way: on the dashboard, open Channels, choose Create with QR under Telegram, and scan the code with your phone. Hermes creates the bot and lets only you use it.
Prefer your own bot? Message @BotFather (check the handle), send
/newbot, and follow the prompts. Message @userinfobot for your
numeric user ID. Give both to Shelley in your private conversation and
ask it to connect Telegram.
Message your bot to check it replies.
Keep the memories#
Memory, skills, conversations, and settings live in ~/.hermes. Nightly
backups go to ~/backups/hermes, and the newest 14 are kept. Copy them
off the VM, too. On your computer:
rsync -av my-hermes-agent.exe.xyz:backups/hermes/ ./hermes-backups/
Keep those copies private: they hold bot tokens and conversations.
Setup and upkeep#
No need to read on unless you're curious—or doing it yourself.
Install by hand
On your computer (pick another name if my-hermes-agent is taken):
ssh exe.dev new --name=my-hermes-agent
ssh my-hermes-agent.exe.xyz
All remaining installation commands run on the VM, as exedev.
Install Hermes#
The official installer brings its own Python, Node.js, and a headless browser, and follows stable releases. A server has no desktop for the computer-use driver to control, so skip that:
curl -fsSL https://hermes-agent.nousresearch.com/install.sh |
bash -s -- --non-interactive --skip-computer-use
export PATH="$HOME/.local/bin:$PATH"
hermes --version
Expect Hermes Agent v0.22 or later. It takes a couple of minutes.
Connect Claude through exe.dev#
The API key below is a placeholder; exe.dev supplies the real credentials
outside the VM. The /anthropic path tells Hermes to speak Claude's own
API. The exe-dev provider reaches the integration's other models:
hermes config set model.provider anthropic
hermes config set model.base_url https://llm.int.exe.xyz/anthropic
hermes config set model.default claude-sonnet-5-5
hermes config set ANTHROPIC_API_KEY exe-llm-integration
hermes config set providers.exe-dev.api https://llm.int.exe.xyz/v1
hermes config set providers.exe-dev.api_key exe-llm-integration
hermes config set providers.exe-dev.transport chat_completions
hermes -z "Reply with exactly: pong"
The agent should answer pong.
Start the gateway#
The gateway connects chat apps and runs scheduled jobs. Lingering keeps your services running after you log out:
sudo loginctl enable-linger "$USER"
hermes gateway install
loginctl show-user "$USER" -p Linger
systemctl --user is-enabled hermes-gateway
systemctl --user is-active hermes-gateway
Expect Linger=yes, enabled, and active. With no chat app yet, its
log says No messaging platforms enabled; that's fine.
Run the dashboard#
The dashboard listens only on the VM. Told its public address, it
insists on a sign-in, so generate a password; it lands in ~/.hermes/.env,
which only you can read:
hermes config set dashboard.public_url "https://$(hostname).exe.xyz"
hermes config set HERMES_DASHBOARD_BASIC_AUTH_USERNAME hermes
grep -q '^HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=' ~/.hermes/.env ||
hermes config set HERMES_DASHBOARD_BASIC_AUTH_PASSWORD "$(openssl rand -hex 12)"
grep -q '^HERMES_DASHBOARD_BASIC_AUTH_SECRET=' ~/.hermes/.env ||
hermes config set HERMES_DASHBOARD_BASIC_AUTH_SECRET "$(openssl rand -base64 32)"
The secret signs sign-in sessions, so they survive restarts. Now run the dashboard as a service:
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/hermes-dashboard.service <<'EOF'
[Unit]
Description=Hermes Agent dashboard
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=%h/.local/bin/hermes dashboard --host 127.0.0.1 --port 9119 --no-open
WorkingDirectory=%h
Restart=always
RestartSec=10
RestartPreventExitStatus=78
[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
systemctl --user enable --now hermes-dashboard
sleep 5
curl -fsS --retry 20 --retry-connrefused --retry-delay 3 \
http://127.0.0.1:9119/api/status | jq '.auth_required, .auth_providers'
Expect true and ["basic"].
Set up the dashboard proxy#
The exe.dev proxy handles HTTPS and login. nginx passes requests to the dashboard, keeping the browser's hostname. This replaces the image's default site:
sudo tee /etc/nginx/sites-available/hermes-agent >/dev/null <<'EOF'
map $http_upgrade $hermes_connection {
default upgrade;
"" close;
}
server {
listen 8000 default_server;
listen [::]:8000 default_server;
server_name _;
location / {
proxy_pass http://127.0.0.1:9119;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $hermes_connection;
proxy_set_header Host $host;
# Overwrite, never append, client attribution.
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_read_timeout 1d;
proxy_send_timeout 1d;
proxy_buffering off;
client_max_body_size 50m;
}
}
EOF
sudo rm -f /etc/nginx/sites-enabled/default
sudo ln -sf /etc/nginx/sites-available/hermes-agent /etc/nginx/sites-enabled/hermes-agent
sudo nginx -t && sudo systemctl enable --now nginx && sudo systemctl reload nginx
curl -fsS -H "Host: $(hostname).exe.xyz" http://127.0.0.1:8000/api/status |
jq '.auth_required, .auth_providers'
Expect true and ["basic"] through nginx, too.
Set up nightly backups#
Hermes's own backup copies everything but the code and caches. Keep two weeks of private archives:
mkdir -p ~/.config/systemd/user ~/backups/hermes
chmod 700 ~/backups ~/backups/hermes
cat > ~/.config/systemd/user/hermes-backup.service <<'EOF'
[Unit]
Description=Hermes Agent backup
[Service]
Type=oneshot
UMask=0077
ExecStart=%h/.local/bin/hermes backup --output %h/backups/hermes/ --keep 14
EOF
cat > ~/.config/systemd/user/hermes-backup.timer <<'EOF'
[Unit]
Description=Nightly Hermes Agent backup
[Timer]
OnCalendar=*-*-* 03:30:00
RandomizedDelaySec=30m
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl --user daemon-reload
systemctl --user enable --now hermes-backup.timer
systemctl --user start hermes-backup.service
unzip -tq "$(ls -t ~/backups/hermes/hermes-backup-*.zip | head -1)"
systemctl --user list-timers hermes-backup.timer
Look for No errors detected and a scheduled next run.
Open the dashboard#
Print your Hermes sign-in:
grep -E '^HERMES_DASHBOARD_BASIC_AUTH_(USERNAME|PASSWORD)=' ~/.hermes/.env
Open https://my-hermes-agent.exe.xyz/, sign in to exe.dev, then to
Hermes, and try the chat.
Connect Telegram by hand (optional)
On the VM, paste the token from @BotFather, then your numeric user ID from @userinfobot—not your @username:
read -rsp "Telegram bot token: " token; echo
hermes config set TELEGRAM_BOT_TOKEN "$token"
unset token
read -rp "Your Telegram user ID: " telegram_id
hermes config set TELEGRAM_ALLOWED_USERS "$telegram_id"
hermes config set TELEGRAM_HOME_CHANNEL "$telegram_id"
unset telegram_id
systemctl --user restart hermes-gateway
sleep 20
systemctl --user is-active hermes-gateway
grep -i telegram ~/.hermes/logs/gateway.log | tail -n 3
Expect active and ✓ telegram connected. Only the allowed user gets
answers; the home channel is where scheduled jobs report. Message your
bot to check. Groups need more settings; see Hermes's
Telegram guide.
Back up and restore
On the VM, take a fresh backup:
(umask 077; hermes backup --output ~/backups/hermes/ --keep 14)
To restore, replace BACKUP.zip with your chosen archive's filename.
The import overwrites the matching files in ~/.hermes, so stop Hermes
first:
systemctl --user stop hermes-gateway hermes-dashboard &&
hermes import --force ~/backups/hermes/BACKUP.zip &&
hermes config set dashboard.public_url "https://$(hostname).exe.xyz" &&
systemctl --user start hermes-gateway hermes-dashboard &&
hermes -z "What do you remember about me?"
The archive brings back its own dashboard password and bot tokens.
Moving to a new VM? Set it up first, copy the archive over with scp,
then restore. Stop the old VM's gateway: a Telegram bot answers only one.
Update
On the VM, back up, then update to the latest stable release. Hermes restarts the gateway and dashboard itself:
(umask 077; hermes backup --output ~/backups/hermes/ --keep 14) &&
hermes update --yes &&
hermes --version &&
systemctl --user is-active hermes-gateway hermes-dashboard
Expect the new version and active twice. Reload the dashboard and try a
chat. Your settings and memory stay put.
Something stuck?
Ask Shelley, or check on the VM:
systemctl --user is-active hermes-gateway hermes-dashboard
journalctl --user -u hermes-gateway -u hermes-dashboard -n 40 --no-pager
hermes -z "Reply with exactly: pong"
hermes: command not found? Runsource ~/.bashrc, or log in again.- 502 from the dashboard? It takes a few seconds to start; reload. Still there? Look for errors in the log above.
Invalid Host headeror a sign-in loop? The dashboard's public address must match the VM's; rerun thedashboard.public_urlline above and restarthermes-dashboard.- Gateway stopped after adding Telegram? A rejected bot token stops
it, and scheduled jobs with it. Set a fresh token from @BotFather, or
hermes config unset TELEGRAM_BOT_TOKEN, then restarthermes-gateway. - Bot ignores you?
TELEGRAM_ALLOWED_USERSmust hold your numeric ID. - No model reply? Check
hermes config get modelagainst the settings above and your LLM integration. hermes doctorsays the Anthropic key is invalid? It tests the placeholder against Anthropic directly. Ifpongcomes back, ignore it, and don't lethermes setupreplace the model settings.
Frequently asked questions#
What does it cost?
Your VM plan plus model usage, billed through the LLM integration. Scheduled jobs use tokens each time they run; the dashboard's Cron page lists them.
Can I use GPT, Kimi, or my own API key?
Yes. In a chat, /model claude-opus-5-5 switches Claude models and /model gpt-5.5 --provider exe-dev reaches the integration's other models; add --global to keep the choice. To bring your own key or subscription, change the LLM integration, or run hermes model on the VM to add any provider Hermes supports.
Can I use Discord, Slack, or WhatsApp instead of Telegram?
Yes. The dashboard's Channels page sets up Hermes's chat apps. Telegram, Discord, and Slack connect outward, so they need no public address; keep each one's allowed-users list to your own IDs.
Why two logins?
exe.dev's login keeps everyone else away from the VM. Hermes also refuses to serve its dashboard beyond the VM itself without its own sign-in, so you get a second lock. Your browser remembers both.
I already use OpenClaw. Can I bring my memories?
Yes. Copy your ~/.openclaw folder to the VM with scp -r. On the VM, preview with hermes claw migrate --preset user-data --dry-run, then import with hermes claw migrate --preset user-data --overwrite. Your persona, memories, and skills come across, and Hermes takes a backup first. Check a chat afterwards.
Does it keep working when my laptop is closed?
Yes. Hermes runs on the VM, not your laptop. Scheduled jobs, chat apps, and the dashboard all carry on, and come back after a reboot.
Updated Oct 17, 2026 · tested end to end Oct 17, 2026 · Markdown · manifest.json · Something wrong? Tell us