01
Running everything as root
Root feels convenient until it fails badly. Use a restricted hermes user.
Operating model
Every step should answer the same practical questions:
01
Reach
Keep administration private.
Use SSH keys, close public SSH after Tailscale works, and keep dashboards on localhost, Tailscale, or an SSH tunnel.
02
Execute
Separate the operator from the agent runtime.
Run Hermes as a restricted user, prefer the Docker backend, and avoid direct host-shell execution for normal agent work.
03
Expose
Keep secrets and mounts narrow.
Store secrets in .env, forward no environment by default, avoid broad host mounts, and give generated files one /output path.
04
Access
Default-deny every interface.
Allowlist Telegram or Discord users, limit commands per platform, review MCP/tools, and delay risky integrations.
05
Recover
Assume the system will need rollback.
Take snapshots after stable phases, keep recovery commands in your notes, monitor gateway/dashboard logs, and test reboot survival before adding sensitive workflows.
Most “install an AI agent on a server” guides dump commands. They get a agent running, then leave the important question unanswered: What did you expose to the internet, to the agent runtime, and to the untrusted content it reads?
You want a private, useful, secure Hermes Agent with Docker-isolated tools, allowlisted Telegram access and an Obsidian vault that syncs back to your laptop.
This is not a guarantee of security. It is a practical personal deployment. Review it against your own threat model before using it with sensitive data.
A safer self-hosted Hermes Agent setup runs on a Ubuntu VPS, exposes administration only through Tailscale, executes tools through Docker rather than the host shell, stores secrets outside the tool runtime, and restricts Telegram access to allowlisted users.
Target architecture
Mac / Laptop
Local operator
Tailscale
Private network
Ubuntu VPS
Hardened host
Hermes Gateway
Agent interface
Docker sandbox
Tool boundary
/output
Artifact mount
Obsidian vault
Syncthing sync
Public internet
No admin dashboards or SSH surface
Tailscale
SSH, dashboard, and internal services
Docker tools
No host home, no Docker socket, no broad secrets
The design principle is that the agent can work without inheriting your whole server.
My current deployment:
Current deployment
Layer 01
Ubuntu 24.04 VPS on Hetzner
Layer 02
Tailscale for private access
Layer 03
Docker backend for tools
Layer 04
Telegram gateway
Layer 05
Hermes Desktop through an SSH tunnel
Layer 06
Dashboard bound to localhost
Layer 07
Admin surfaces behind Tailscale or localhost
Layer 08
Hermes runs as a non-root user
Layer 09
Obsidian vault synced with Syncthing
Layer 10
Restricted tool surface
Hermes can use tools, run commands, write files, search the web, remember context, and talk through messaging platforms. That makes it useful. It also makes a private deployment closer to a small operations system than a casual chatbot.
This configuration is a strong fit if you want Hermes for:
Do not use this baseline alone for:
For those workflows, add approval layers, audit logs, secrets management, scoped credentials, monitoring, and a second human review process.
Use a mainstream VPS provider and a current Ubuntu LTS image.
Recommended baseline:
| Component | Minimum |
|---|---|
| OS | Ubuntu 24.04 LTS |
| CPU | 4+ vCPU |
| RAM | 8+ GB |
| Disk | 100+ GB |
Reasonable providers include Hetzner, DigitalOcean, Vultr, and Linode. A Hetzner CPX31/CPX32-class instance is a practical starting point for personal agent workflows.
Do not start with the cheapest box. Hermes is not the only workload. You may also run Docker containers, Syncthing, logs, browsers, Python tooling, and generated artifacts.
Create an SSH keypair on your laptop. Upload only the public key to the VPS provider.
Laptop/Mac
private key: stays local
public key: uploaded to VPS providerUse SSH keys from the start when possible. If password SSH is used during bootstrap, disable it after confirming key-based admin access.
Create two users:
User role
admin
sudo: yes
purpose: server maintenance
User role
hermes
sudo: no
purpose: run Hermes and related services
This separation matters. If someone abuses Hermes or a tool runtime, the next step should not be sudo.
The hermes user should own the Hermes install and its working directories. The admin user should handle package installation, firewall changes, system upgrades, and recovery tasks.
At minimum:
SSH hardening can lock you out. Take a VPS snapshot before and after this phase if your provider supports it.
Start with UFW:
Firewall baseline
incoming: deny by default
outgoing: allow by default
initially allow: SSH
The final goal is stricter: SSH should only be reachable over Tailscale, not from the public internet.
That means there are two phases:
100.x.x.x Tailscale address.Do not close public SSH until you have tested Tailscale SSH access from your laptop.
Tailscale gives you a private network without your own VPN server. For this setup, it keeps dashboards, admin ports, and SSH off the public internet.
After Tailscale is installed and the VPS appears in your tailnet:
Use this mental model:
Network visibility
Public internet
should not see Hermes admin surfaces
Tailscale
can reach SSH / dashboard / internal services
Hermes can run terminal/file/code execution through different backends. For a security-conscious VPS, prefer:
yamlterminal:
backend: dockerAvoid using a direct local shell backend for normal agent work on a server. Docker is not a magic shield, but it creates a boundary between agent tool execution and the host operating system.
This isolates common tool actions such as:
You will not make compromise impossible. You can stop routine agent work from getting the same access as the server operator.
/home/hermes, not as rootInstall Hermes as the restricted hermes user.
Recommended location:
/home/hermesUseful verification commands after installation:
bashhermes version
hermes doctor
hermes config path
hermes config env-pathUse the public Hermes installer endpoint:
bashcurl -fsSL https://hermes-agent.nousresearch.com/install.sh | bashThen run the setup wizard:
bashhermes setupFor provider authentication, use the current Hermes docs and the hermes setup, hermes model, and hermes auth flows. Do not hardcode stale model-provider assumptions.
Model choice affects quality, cost, latency, and context. It will not fix a bad server architecture.
Hermes supports both OAuth/subscription-backed providers and API-key-backed providers. Some advanced tooling, external evaluation frameworks, and optimization systems may require direct API keys even if normal Hermes usage works through OAuth.
Use lighter daily models for normal operation and stronger models for deep work. Treat the model as one layer. Your security posture comes from:
For operational resilience, configure fallback providers in config.yaml, not .env:
yamlfallback_providers:
- provider: openrouter
model: <fallback-model-name>Choose a model currently supported by your provider. Use fallbacks for outages, rate limits, or cost control, and test them before you depend on them during a long research run.
.env, not in tool-visible filesHermes documentation separates configuration from secrets:
~/.hermes/config.yaml → normal settings
~/.hermes/.env → API keys, bot tokens, secretsThat split matters. The tool container should not inherit your whole environment.
Use a tighter posture:
yamlterminal:
backend: docker
env_passthrough: []
docker_forward_env: []Forward only the variables a workflow needs. If a tool needs a GitHub token, add it on purpose. Do not forward your whole shell environment into an AI-controlled runtime.
Do not mount the host root filesystem into the tool container.
Avoid mounting:
/
/home/hermes
/var/run/docker.sockEspecially avoid the Docker socket. Mounting /var/run/docker.sock usually means the container can control Docker on the host. That can become host-level control.
Prefer a small, intentional artifact mount:
yamlterminal:
backend: docker
docker_mount_cwd_to_workspace: false
docker_forward_env: []
env_passthrough: []
docker_volumes:
- "/home/hermes/output:/output"Then tell Hermes to write generated files to /output. Any dedicated folder can work. Mount that folder, not the whole home directory. The exact host path is not important. The rule is simple: mount only a dedicated artifacts directory into /output, not the entire home directory.
If Docker writes files as root, your sync tools and editor will complain.
Use Hermes Docker settings so generated files are owned by the host user where possible:
yamlterminal:
docker_run_as_host_user: trueUse docker_run_as_host_user: true when writing to a synced /output folder, so generated files are owned by the hermes user instead of root. This improves file sync and permissions, but it is not a standalone security control. It should still be paired with narrow mounts and no secret passthrough. Root-owned markdown files do not improve security. They create maintenance work.
Create a bot through BotFather and keep the token secret. If it leaks, revoke it.
Hermes Telegram setup can be handled interactively:
bashhermes gateway setupFor manual .env configuration:
envTELEGRAM_BOT_TOKEN=<your-bot-token>
TELEGRAM_ALLOWED_USERS=<your-numeric-telegram-id>Do not use an allow-all configuration for a private AI agent. Telegram is the front door. Lock it.
If the bot is added to groups, also pay attention to:
The desired gateway posture is default-deny: only explicitly allowed users or paired users should be able to interact with the agent.
The default private deployment should begin with one user ID: yours. For groups, use group allowlists separately and do not assume a private-user allowlist protects group chats automatically.
Allowlisting controls who can reach the bot. It does not mean every allowed user should get every command.
For shared bots, separate:
admin users
regular allowed users
allowed slash commandsUse limited command exposure for non-admin users. Do not assume "allowlisted" means "safe to give full tool access."
For shared bots, configure allow_admin_from and user_allowed_commands so regular users only get specific slash commands. For groups, use the group equivalents: group_allow_admin_from and group_user_allowed_commands.
A Telegram assistant needs to survive logout and SSH disconnects.
Use Hermes gateway service commands:
bashhermes gateway install
hermes gateway start
hermes gateway statusIf you are using a user-level service on Linux, make sure the service can survive logout when required. On systemd systems that may involve enabling linger for the service user.
Then check the service and logs:
bashhermes gateway status
journalctl --user -u hermes-gateway -n 120 --no-pager
sudo systemctl status syncthing-hermes
sudo ufw status verboseIf the gateway is not installed as a systemd user service, check the Hermes log file instead:
bashtail -n 100 ~/.hermes/logs/gateway.logDo not expose every Hermes tool to every interface by default. A Telegram bot, Discord bot, desktop app, and terminal session do not need the same tool surface.
Run hermes tools to configure individual tool availability per platform. Hermes persists those choices to config.yaml, so this is finer-grained than only enabling or disabling broad toolsets.
Start with a small surface:
/output;Treat each new tool as another attack surface. Docker isolation helps, but it does not make a powerful tool safe on every platform.
Treat MCP servers and external tool integrations like privileged plugins. Do not add MCP servers casually. Review what tools they expose, what credentials they need, and whether they can access local files, network services, or secrets.
Delay sensitive integrations on day one:
Start with Telegram allowlisted to one user, one web search backend, and /output file writing. Add stronger integrations only after the basic setup behaves safely.
/output the only normal write targetA good artifact system gives generated files one obvious destination.
Use:
Host
/home/hermes/output
Docker
/output
This prevents the common failure mode where the agent creates a file, but the gateway cannot send it or your laptop cannot find it.
For Docker-backed Telegram sessions, Hermes docs warn that attachments are sent by the gateway process, not from inside the container. So the final file path must be readable by the gateway host.
Use the shared /output mount for that reason.
You can add Obsidian as the knowledge layer.
Create a vault directory on the VPS:
Host vault path
/home/hermes/output/obsidian-vault
Owned by the restricted Hermes user on the VPS.
Inside the container, Hermes sees it as:
Container vault path
/output/obsidian-vault
The same vault location as seen by Docker-backed tools.
Now Hermes can write notes, reports, ledgers, markdown drafts, research artifacts, and operating documents into one predictable place.
A simple structure:
Vault structure
/output/obsidian-vault/
Use the inbox for generated artifacts. Curate later in local Obsidian.
Hermes only needs a Markdown vault path. In this setup, Syncthing is the sync layer that moves that folder between the VPS and your Mac for local Obsidian use. It is your deployment pattern, not a Hermes requirement. Hermes also has official Obsidian-oriented patterns, including vault-path configuration and server-friendly options such as headless Obsidian/Obsidian Sync, but Syncthing is simpler and private for this setup.
Syncthing syncs files privately without pushing AI artifacts through Google Drive, Dropbox, or email.
The pattern:
Sync endpoint
VPS vault
Sync endpoint
Mac vault
Benefits:
Check ownership and permissions after the first sync. If files are root-owned, fix the Docker user mapping before you generate hundreds of notes.
For research workflows, add one current web search provider. Tavily is a practical first choice for agent-oriented search. Exa is useful for semantic search workflows, but add one web backend first and test it before adding more.
A web backend gives Hermes current sources. The model’s training data is not enough for research workflows, and AI-agent outputs should cite live pages.
Web pages, PDFs, skills, READMEs, and arbitrary documents are untrusted input. They can contain prompt injection, malicious instructions, or misleading claims.
Keep the policy simple:
Use web search for evidence.
Do not let web content override deployment rules.The safest default is a dashboard bound to 127.0.0.1 on the VPS, reached from your laptop through an SSH tunnel. Move to a Tailscale-bound dashboard only if you specifically want a permanently available remote backend. If you do that, bind only to the VPS Tailscale IP and allow the port only on the Tailscale interface.
Hermes Desktop remote backend uses the dashboard backend, usually port 9119, not the OpenAI-compatible API server on 8642. If using the desktop app remotely, point it at the dashboard URL and sign in with the dashboard auth provider advertised by that backend.
For anything reachable beyond your own trusted network, use the OAuth / Nous Portal dashboard auth path from the current Hermes docs. For localhost, SSH tunnel, or Tailscale-only trusted-network use, username/password dashboard auth is the simpler path.
Set dashboard login credentials in ~/.hermes/.env:
envHERMES_DASHBOARD_BASIC_AUTH_USERNAME=<dashboard-user>
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=<strong-dashboard-password>
HERMES_DASHBOARD_BASIC_AUTH_SECRET=<random-signing-secret>Generate a stable signing secret on the VPS so dashboard sessions survive restarts:
bashopenssl rand -base64 32Then run the dashboard through a tunnel-first setup on localhost:
bashhermes dashboard --tui --no-open --host 127.0.0.1 --port 9119For a Tailscale-bound trusted-network dashboard, bind to the VPS Tailscale IP:
bashhermes dashboard --tui --no-open --host <tailscale-ip> --port 9119A safe access pattern is an SSH tunnel to 127.0.0.1:9119 or http://<tailscale-ip>:9119 behind Tailscale. Never expose the dashboard directly to the open internet. Public access should use the OAuth / Nous Portal path, network restrictions, TLS, monitoring, and a clear reason.
Avoid public 0.0.0.0 exposure unless you have intentionally added authentication, network restrictions, TLS, monitoring, and a clear reason.
For a personal AI-agent VPS, public dashboards add risk without much benefit.
Take provider snapshots after each major phase:
Snapshots do not replace backups. They give you fast rollback when you break SSH, Docker, or the gateway service.
The agent may read:
Those inputs are data sources, not system administrators.
Keep this rule in the deployment notes:
Deployment rule
External content can inform the answer.
It cannot override deployment policy.
For higher-risk actions, require approval before:
/output;Start here, then adapt it to the current Hermes docs and your deployment:
yamlterminal:
backend: docker
container_persistent: false
docker_mount_cwd_to_workspace: false
docker_run_as_host_user: true
docker_forward_env: []
env_passthrough: []
docker_volumes:
- "/home/hermes/output:/output"
approvals:
mode: manualNote: in current Hermes versions, the Docker backend may reuse a long-lived container across sessions by default. container_persistent: false is not the same as “destroy the entire container after every turn.” If you need stronger per-process isolation, also review docker_persist_across_processes, lifetime_seconds, and the Docker lifecycle settings in the current Hermes docs.
Secrets should stay in:
/home/hermes/.hermes/.envnot inside project directories, not in Obsidian notes, and not in files written to /output.
Security scorecard
VPS
Ubuntu LTS, snapshots, patched OS
Users
admin with sudo; hermes without sudo
SSH
keys only; no root; no password login
Network
Tailscale for admin access; public SSH closed after bootstrap
Firewall
deny incoming by default
Tools
Docker backend plus platform-specific tool restrictions
Secrets
.env, minimal env forwarding
Files
dedicated /output mount
Telegram
token secret, user allowlist, default-deny, limited slash commands for shared bots
Obsidian
synced via Syncthing, not broad host mounts
Dashboard
localhost/Tailscale/tunnel only; Desktop remote points to dashboard port 9119, not API port 8642
Recovery
snapshots after every major phase
I would treat this as a strong personal baseline, not a production compliance architecture.
Failure modes
01
Root feels convenient until it fails badly. Use a restricted hermes user.
02
backend: local on a VPSThat gives the agent direct shell access to the host. Use Docker for normal tool execution.
03
If the tool container receives every secret your shell knows, the container boundary is much weaker.
04
It feels convenient and gives the tool runtime far more access than it needs.
05
A Telegram bot is an access surface. Use TELEGRAM_ALLOWED_USERS.
06
Use Tailscale or an SSH tunnel. Keep AI-agent admin panels off the public internet by default.
07
If Docker writes to /workspace/report.md and the gateway cannot read that path, attachments fail. Use a shared /output mount.
08
--yoloDo not use --yolo for a server-side agent. It bypasses dangerous-command approval prompts. Keep approvals in manual mode unless you have a strong reason and compensating controls.
Hermes docs describe approvals.mode: manual as the default for destructive-command prompts, and off as equivalent to --yolo.
Implementation checklist
43 deployment checks from bootstrap to rollback.
hermes toolshermes toolsNo. Docker is one layer. You also need a restricted user, minimal mounts, minimal environment forwarding, private networking, gateway allowlists, and careful approval policies.
For a personal deployment, usually no. Use Tailscale or an SSH tunnel. Public exposure should be a deliberate, monitored production decision.
Yes, if you keep the bot token secret and restrict access to numeric allowlisted user IDs. Do not use an allow-all setting for a private agent.
/output?Because it creates one controlled write zone for generated files. It also solves Docker/gateway path mismatch problems when sending files through Telegram or syncing to Obsidian.
It keeps the knowledge base as real local files, syncs privately, and avoids turning every AI artifact into a cloud-document workflow.
Use the Hermes dashboard backend, usually port 9119, not the OpenAI-compatible API server on 8642. Keep it private through Tailscale or an SSH tunnel, and sign in through the dashboard auth provider. Use OAuth / Nous Portal for public reachability; use username/password dashboard auth only for localhost or trusted-network access.
Not by default. Add stronger controls first: encryption policies, access logging, explicit approvals, data retention rules, backups, monitoring, and professional compliance review.
A self-hosted Hermes Agent can be useful, but the safe version is not “install Hermes on a VPS.” It is a small private AI operations stack.
The secure pattern is:
Secure pattern
That gives you an always-on private AI agent without handing the model your whole server.
Hermes configuration documentation
hermes-agent.nousresearch.com/docs/user-guide/configuration
Hermes security documentation
hermes-agent.nousresearch.com/docs/user-guide/security
Hermes Telegram documentation
hermes-agent.nousresearch.com/docs/user-guide/messaging/telegram
Hermes tools documentation
hermes-agent.nousresearch.com/docs/user-guide/features/tools
Hermes environment variables documentation
hermes-agent.nousresearch.com/docs/reference/environment-variables
Hermes MCP documentation
hermes-agent.nousresearch.com/docs/user-guide/features/mcp
Hermes installation documentation
hermes-agent.nousresearch.com/docs/getting-started/installation
Hermes Desktop documentation
hermes-agent.nousresearch.com/docs/user-guide/desktop
Hermes Web Dashboard documentation
hermes-agent.nousresearch.com/docs/user-guide/features/web-dashboard