Samen Steeve
MY SERVICES.
Back to blog
AIDevOpsAgentsSecurityDocker
Deploying an Autonomous AI Agent in Production: Hermes Agent Retrospective
July 24, 20268 min read

Deploying an Autonomous AI Agent in Production: Hermes Agent Retrospective

Autonomous, self-hosted AI agents have become a compelling alternative to traditional SaaS assistants: full data control, no recurring fees, and deep integration into existing infrastructure. I wanted to test this under real production conditions with Hermes Agent, an open-source (MIT) project by Nous Research running as a persistent daemon with long-term memory, cron tasks, and a multi-platform gateway (Telegram, Discord, Slack, WhatsApp, etc.).

Tech Stack & Infrastructure Tools
Hermes Agent (Nous Research)Hermes WebUIsystemdNginxLet's EncryptrcloneGoogle DriveUFW

Why This Project

The goal wasn't just running a five-minute curl | bash script — anyone can do that. The goal was deploying it production-style: proper user isolation, reduced attack surface, credential encryption, and a backup strategy resilient to server failures.

This guide documents every step, including real-world troubleshooting encounters — because real deployments never go strictly according to initial docs.

Final Architecture

Internet
   │
   ▼
┌─────────────┐      HTTPS (Let's Encrypt)
│   Nginx     │──────────────────────────┐
└─────────────┘                          │
   │ reverse proxy (127.0.0.1)           │
   ▼                                     ▼
┌─────────────────┐            ┌──────────────────┐
│ Hermes Gateway   │            │  Hermes WebUI     │
│ (Telegram)       │            │  (dashboard chat) │
│ restricted tools │            │  password auth    │
└─────────────────┘            └──────────────────┘
   │                                     │
   └──────────────┬──────────────────────┘
                   ▼
         ┌───────────────────┐
         │  Hermes Agent Core │
         │  (dedicated user,  │
         │   no sudo access) │
         └───────────────────┘
                   │
                   ▼
      ┌────────────────────────┐
      │  Daily Cron at 03:00   │
      │  hermes backup → zip    │
      │  rclone → Google Drive  │
      │  (encrypted config)     │
      └────────────────────────┘

Two separate systemd services (hermes-gateway.service and hermes-webui.service), a dedicated non-sudo system user, and an automated backup pipeline replicating to encrypted remote storage.

1. Core Installation

Hermes provides a one-liner installer managing runtime dependencies (Python 3.11, Node.js, ripgrep, ffmpeg) automatically:

Terminal — Setup
Bash
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
source ~/.bashrc
hermes setup

The interactive wizard configures LLM providers (Anthropic Claude, OpenRouter, Nous Portal, or any OpenAI-compatible endpoint). At this stage everything runs — but under root, without firewall policies, and with unrestricted tools.

2. Agent Isolation: Root → Dedicated User Migration

Running an agent with terminal access, code execution, and network access as rootis a shortcut that feels fine right up until it isn't. Step one: creating a dedicated unprivileged user.

Terminal — User Creation
Bash
adduser hermes

Migrating data directories (~/.hermes and ~/.local/state/hermes), fixing ownership, and updating the systemd unit:

/etc/systemd/system/hermes-gateway.service
Systemd
[Unit]
Description=Hermes Agent Gateway Service
After=network.target

[Service]
Type=simple
User=hermes
Group=hermes
ExecStart=/usr/local/lib/hermes-agent/venv/bin/python -m hermes_cli.main gateway run
WorkingDirectory=/home/hermes/.hermes
Environment="HERMES_HOME=/home/hermes/.hermes"
Restart=always

[Install]
WantedBy=multi-user.target
⚠️ Gotcha EncounteredAfter migration, running hermes config show as root printed empty configuration — expected, as the CLI inspects the active user's $HOME. Always verify with sudo -u hermes -i.

3. Hardening Telegram Bot Attack Surface

This is the single most critical security hardening step: by default, messaging platforms share the full CLI toolset. Anyone paired via Telegram could execute arbitrary shell commands, code, and browse your network.

config.yaml snippet before hardening:

config.yaml — Before restriction
YAML
platform_toolsets:
  telegram:
    - terminal
    - code_execution
    - computer_use
    - browser
    - delegation
    - file
    - memory
    - web
    # ...

After hardening (retaining only essential messaging tools):

config.yaml — After restriction
YAML
platform_toolsets:
  telegram:
    - clarify
    - cronjob
    - file
    - image_gen
    - memory
    - session_search
    - skills
    - todo
    - tts
    - vision
    - web

terminal, code_execution, computer_use, browser, and delegation were stripped. delegation was explicitly removed because delegated sub-agents could otherwise invoke blocked tools.

In addition, security guardrails were explicitly enabled:

config.yaml — Security Policies
YAML
security:
  redact_secrets: true      # Redacts API keys / tokens from logs & responses
  tirith_enabled: true      # Pre-execution command inspector
  tirith_fail_open: false   # Blocks (instead of allowing) if inspector fails

Setting fail_open: false is intentional: default permissive fallbacks on public facing bots are unsafe. Pairing controls (hermes pairing approve telegram <code>) ensure only explicitly approved user IDs interact with the agent.

4. Web Dashboard: Official vs Community WebUI

Hermes includes an internal dashboard (hermes dashboard, port 9119), but I deployed Hermes WebUI (community MIT project) for rich chat features, session tracking, streaming, and file browsing.

Using third-party tooling requires risk trade-offs offset here by: loopback binding only, strict password authentication, and HTTPS reverse proxy encapsulation.

Screenshot: Hermes WebUI running in active production on hermes.samensteeve.comwith multi-channel sessions (WebUI & Telegram).

Terminal — WebUI Setup
Bash
git clone https://github.com/nesquena/hermes-webui.git
cd hermes-webui
python3 bootstrap.py
hermes-webui/.env
Config
HERMES_WEBUI_HOST=127.0.0.1
HERMES_WEBUI_PASSWORD=<strong-generated-password>
HERMES_WEBUI_SECURE=1
HERMES_WEBUI_ALLOWED_ORIGINS=https://hermes.yourdomain.com

Three Troubleshooting Lessons:

  1. Python Interpreter Mismatch: The service initially used system Python (/usr/bin/python3) missing agent dependencies (httpx, dotenv). Solution: point ExecStart to the agent virtualenv directly.
  2. Orphaned Zombie Processes: A failed restart left port 8787 locked (FATAL: Another server is already responding on 127.0.0.1:8787). Resolved via ss -tlnp PID identification and cleanup.
  3. Malformed YAML Syntax: Manual edit left an unindented commented key header (# security:) breaking the YAML parser. Always validate via python3 -c "import yaml; yaml.safe_load(...)" before restarting services.

Nginx Reverse Proxy + Let's Encrypt SSL:

/etc/nginx/sites-available/hermes
Nginx
server {
    listen 80;
    server_name hermes.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:8787;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
Terminal — Certbot TLS
Bash
sudo certbot --nginx -d hermes.yourdomain.com

5. Encrypted, Automated Remote Backups

An autonomous agent accumulates long-term state, memory, and custom skills over time. Strategy: daily local snapshot + Google Drive remote sync with 14-day retention.

/home/hermes/scripts/backup-hermes.sh
Bash Script
#!/bin/bash
set -euo pipefail
cd /home/hermes

export RCLONE_CONFIG_PASS="********"   # Encrypted rclone config password

BACKUP_DIR="/home/hermes/backups"
DATE=$(date +%Y%m%d-%H%M%S)
KEEP_DAYS=14
GDRIVE_REMOTE="gdrive:hermes-backups"

hermes backup -o "$BACKUP_DIR/hermes-backup-$DATE.zip" -l "daily-cron"
rclone copy "$BACKUP_DIR/hermes-backup-$DATE.zip" "$GDRIVE_REMOTE" --quiet

find "$BACKUP_DIR" -name "hermes-backup-*.zip" -mtime +$KEEP_DAYS -delete
rclone delete "$GDRIVE_REMOTE" --min-age ${KEEP_DAYS}d --quiet
crontab -e (hermes user)
Cron
0 3 * * * /home/hermes/scripts/backup-hermes.sh >> /home/hermes/backups/backup.log 2>&1

Key Takeaways & Technical Proof

  • System Hardening: Privilege separation, permission enforcement, process isolation
  • Network Architecture: Reverse proxying, TLS termination, loopback interface bounds
  • Secrets Management: At-rest encryption, environment injection, automatic log redaction
  • Reliable Automation: systemd units, cron scheduling, idempotent shell scripting (set -euo pipefail)
  • Methodical Debugging: journalctl investigation, root cause isolation

Need to integrate AI agents?

Let's secure and orchestrate your autonomous agents in production without logical flaws.

Start a project