Hermes Agent — Full Setup Guide

Deploy

WSL (Ubuntu 24.04) • Docker • Tailscale Funnel • WordPress/WooCommerce • Telegram Bot Gateway

What you’ll learn
Privacy / placeholder note: this guide is public. Values like <user>, <YOUR_TELEGRAM_USER_ID>, <BOT_TOKEN>, <your-agentmail-inbox>, and <your-tailscale-host>.ts.net are placeholders that stand in for the real values on your own machine — usernames, user/chat IDs, bot tokens, hostnames, and home paths differ on every install and have been removed for privacy. Replace each <placeholder> with the matching value for your system before running any command.
Contents
  1. System Overview
  2. Prerequisites
  3. WSL Setup (Windows)
  4. Hermes Agent Installation
  5. Hermes Configuration
  6. WordPress + WooCommerce (Docker)
  7. Tailscale + Funnel
  8. Telegram Bot Gateway
  9. Systemd Service & Cron Watchdog
  10. Verification Checklist
  11. Troubleshooting
  12. Key File Inventory

1. System Overview

This document captures the complete Hermes Agent setup running on a Windows machine via WSL2. It is designed so that if the entire environment is wiped, you can rebuild from scratch by following these instructions step by step.

ComponentValue
Host OSWindows (WSL2 guest)
WSL DistroUbuntu 24.04.1 LTS
WSL Kernel6.18.33.2-microsoft-standard-WSL2
Hermes Versionv0.21.0 (2026.8.31)
DockerDocker Engine 29.1.3 + Compose 2.40.3
Tailscale Hostname.ts.net
LLM ProviderOpenRouter — deepseek/deepseek-v4-flash
Email (AgentMail)@agentmail.to
Telegram Bot@
User Home/home/
Architecture: Hermes runs directly on WSL (no Docker container for Hermes itself). The Telegram gateway is a systemd user service. WordPress, MySQL, n8n, and AFFiNE run as Docker Compose stacks. Tailscale Funnel exposes all services publicly via the ts.net hostname.

2. Prerequisites

3. WSL Setup (Windows)

3.1 Install WSL2

Run in PowerShell as Administrator:

wsl --install -d Ubuntu-24.04

3.2 System Packages

Inside the WSL terminal:

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget git unzip gnupg lsb-release ca-certificates

4. Hermes Agent Installation

4.1 Install Dependencies

sudo apt install -y python3 python3-venv nodejs npm

4.2 Install Hermes

git clone https://github.com/NousResearch/hermes-agent.git /home//.hermes/hermes-agent
cd /home//.hermes/hermes-agent
python3 -m venv venv
source venv/bin/activate
pip install -e .

4.3 Initialize Hermes

hermes init
hermes setup

Follow the interactive prompts to set OpenRouter as the provider with model deepseek/deepseek-v4-flash.

4.4 Verify Installation

hermes --version
# Should print: Hermes Agent v0.21.0

5. Hermes Configuration

5.1 OpenRouter API Key

Set in /home//.hermes/.env:

OPENROUTER_API_KEY=sk-or-v1-YOUR_OPENROUTER_KEY

5.2 config.yaml

Located at /home//.hermes/config.yaml. Key settings:

model:
  default: deepseek/deepseek-v4-flash
  provider: openrouter
  base_url: https://openrouter.ai/api/v1
  
agent:
  max_turns: 150
  reasoning_effort: medium

platforms:
  telegram:
    enabled: true
    home_channel:
      platform: telegram
      chat_id: ""
      name: 
      user_id: ""

mcp_servers:
  agentmail:
    url: "https://mcp.agentmail.to/mcp"
    headers:
      x-api-key: "${AGENTMAIL_API_KEY}"

5.3 AgentMail MCP Setup

AgentMail provides email for AI agents. Inbox: @agentmail.to

# Set in .env:
AGENTMAIL_API_KEY=your_agentmail_key

5.4 Postgres (pgvector) — Optional, for AI memory

sudo apt install -y postgresql-16 postgresql-16-pgvector
sudo -u postgres createuser app -P
sudo -u postgres createdb app -O app
sudo -u postgres createdb ai -O app

Credentials stored in .env:

POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USER=app
DATABASE_URL=postgresql://app:[email protected]:5432/app
DATABASE_URL_AI=postgresql://app:[email protected]:5432/ai

5.5 Complete .env Structure

/home//.hermes/.env contains:

OPENROUTER_API_KEY=sk-or-v1-...
TELEGRAM_BOT_TOKEN= (from @BotFather)
TELEGRAM_ALLOWED_USERS=
HERMES_TELEGRAM_DISABLE_FALLBACK_IPS=1
TELEGRAM_HOME_CHANNEL=
TELEGRAM_WEBHOOK_SECRET= (32 hex chars)
AGENTMAIL_API_KEY=...
SUDO_PASSWORD=...
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USER=app
POSTGRES_PASSWORD=...
DATABASE_URL=postgresql://app:[email protected]:5432/app
DATABASE_URL_AI=postgresql://app:[email protected]:5432/ai
Critical WSL Fix: The env var HERMES_TELEGRAM_DISABLE_FALLBACK_IPS=1 is REQUIRED in WSL. Without it, the Telegram gateway hangs forever on "Connecting to Telegram (attempt 1/8)" due to a DNS-over-HTTPS fallback IP discovery issue specific to WSL.

6. WordPress + WooCommerce (Docker)

6.1 Project Directory

mkdir -p /home//theblessedhoneybee-wp
cd /home//theblessedhoneybee-wp

6.2 Docker Compose File

File: /home//theblessedhoneybee-wp/docker-compose.yml

services:
  db:
    image: mysql:8.0
    restart: always
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - db_data:/var/lib/mysql
  wordpress:
    depends_on:
      - db
    image: wordpress:latest
    restart: always
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wp_data:/var/www/html
volumes:
  db_data: {}
  wp_data: {}

6.3 Start WordPress

cd /home//theblessedhoneybee-wp && docker compose up -d
docker compose ps
# Expect: theblessedhoneybee-wp-wordpress-1 and theblessedhoneybee-wp-db-1

6.4 Install WP-CLI

docker exec theblessedhoneybee-wp-wordpress-1 bash -lc 'curl -sSfL https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar -o /usr/local/bin/wp && chmod +x /usr/local/bin/wp && wp --info'

6.5 WordPress Core Install

docker exec theblessedhoneybee-wp-wordpress-1 wp core install \
  --path=/var/www/html --allow-root \
  --url=http://localhost:8080 \
  --title="The Blessed Honey Bee" \
  --admin_user=admin --admin_password= \
  [email protected]

6.6 WooCommerce

docker exec theblessedhoneybee-wp-wordpress-1 wp plugin install woocommerce --activate --allow-root
docker exec theblessedhoneybee-wp-wordpress-1 wp option update woocommerce_currency AUD --allow-root
docker exec theblessedhoneybee-wp-wordpress-1 wp option update permalink_structure "/%postname%/" --allow-root
docker exec theblessedhoneybee-wp-wordpress-1 wp rewrite flush --allow-root

6.7 Custom Theme

Theme: blessed-honeybee (active custom theme). Install by copying to wp-content/themes/:

docker cp /path/to/theme theblessedhoneybee-wp-wordpress-1:/var/www/html/wp-content/themes/blessed-honeybee/
docker exec theblessedhoneybee-wp-wordpress-1 wp theme activate blessed-honeybee --allow-root

6.8 URL Migration for External Access

Since the site is exposed via Tailscale Funnel, update the WordPress URL:

docker exec theblessedhoneybee-wp-wordpress-1 wp option update siteurl "http://.ts.net" --allow-root
docker exec theblessedhoneybee-wp-wordpress-1 wp option update home "http://.ts.net" --allow-root
docker exec theblessedhoneybee-wp-wordpress-1 wp search-replace 'http://localhost:8080' 'http://.ts.net' --allow-root --all-tables

6.9 Current Versions (as of Sep 2026)

ComponentVersion
WordPress7.1
WooCommerce11.0.1
MySQL8.0
Themeblessed-honeybee (custom)

7. Tailscale + Funnel

7.1 Install Tailscale

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
# Follow the browser auth link to connect to your tailnet

7.2 Set Operator (Passwordless sudo for funnel)

sudo tailscale set --operator=$USER
# Now you can run 'tailscale funnel' without sudo

7.3 Enable Funnel (Root Path — WordPress)

tailscale funnel --bg --set-path=/ http://127.0.0.1:8080

7.4 Enable Funnel (Path Routes)

tailscale funnel --bg --set-path=/telegram http://127.0.0.1:8443
tailscale funnel --bg --set-path=/n8n http://127.0.0.1:5678
tailscale funnel --bg --set-path=/affine http://127.0.0.1:3010

7.5 Verify Funnel Routes

tailscale funnel status

Expected output:

https://.ts.net (Funnel on)
|-- /         proxy http://127.0.0.1:8080   (WordPress)
|-- /n8n      proxy http://127.0.0.1:5678   (n8n)
|-- /affine   proxy http://127.0.0.1:3010   (AFFiNE)
|-- /telegram proxy http://127.0.0.1:8443   (Telegram Gateway)

https://.ts.net:8443 (Funnel on)
|-- / proxy http://127.0.0.1:8443
Funnel Routing Table:
Public URLPathDestinationService
*..ts.net/:8080WordPress
*..ts.net/n8n:5678n8n
*..ts.net/affine:3010AFFiNE
*..ts.net/telegram:8443Telegram Gateway
*..ts.net:8443/:8443Telegram Gateway (direct)

8. Telegram Bot Gateway

8.1 Create the Bot

  • Open Telegram, search for @BotFather
  • Send /newbot, choose a name (e.g. "'s PC Bot")
  • Username: @
  • Copy the bot token (format: )

8.2 Get Your User ID

  • Search @userinfobot on Telegram
  • Send /start — get numeric ID:

8.3 Configure in .env

TELEGRAM_BOT_TOKEN=
TELEGRAM_ALLOWED_USERS=
HERMES_TELEGRAM_DISABLE_FALLBACK_IPS=1
TELEGRAM_HOME_CHANNEL=
TELEGRAM_WEBHOOK_SECRET=     # openssl rand -hex 32

8.4 Start the Gateway

hermes gateway start
# Or restart after config changes:
hermes gateway restart

8.5 Verify Gateway is Listening

ss -tlnp | grep 8443
# Should show python process listening on :8443

8.6 Verify with Telegram API

curl -s "https://api.telegram.org/bot$(grep TELEGRAM_BOT_TOKEN /home//.hermes/.env | cut -d= -f2)/getWebhookInfo"

Expected: JSON with url pointing to https://.ts.net/telegram and no last_error field.

8.7 Set Webhook (if needed manually)

curl -s "https://api.telegram.org/bot$TOKEN/setWebhook?url=https://.ts.net/telegram&secret_token=$WEBHOOK_SECRET"
WSL-Specific Issue: The Telegram gateway may get stuck on "Connecting to Telegram (attempt 1/8)" without the HERMES_TELEGRAM_DISABLE_FALLBACK_IPS=1 environment variable. If the gateway cycles with exit code 75 (TEMPFAIL), check that this variable is set.

9. Systemd Service & Cron Watchdog

9.1 Systemd User Service

File: /home//.config/systemd/user/hermes-gateway.service

[Unit]
Description=Hermes Agent Gateway - Messaging Platform Integration
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=0

[Service]
Type=simple
ExecStart=/home//.hermes/hermes-agent/venv/bin/python -m hermes_cli.main gateway run
WorkingDirectory=/home//.hermes
Environment="PATH=/home//.hermes/hermes-agent/venv/bin:..."
Environment="VIRTUAL_ENV=/home//.hermes/hermes-agent/venv"
Environment="HERMES_HOME=/home//.hermes"
Environment="HERMES_SUPERVISED_CHILD=1"
Restart=always
RestartSec=5
RestartForceExitStatus=75
RestartPreventExitStatus=78
KillMode=mixed
KillSignal=SIGTERM
TimeoutStopSec=70

[Install]
WantedBy=default.target
systemctl --user enable hermes-gateway.service
systemctl --user start hermes-gateway.service
systemctl --user status hermes-gateway.service

9.2 Funnel Watchdog Script

File: /home//.hermes/scripts/ensure_tailscale_funnel.sh

#!/usr/bin/env bash
# Checks BOTH Tailscale connectivity AND funnel route.
set -uo pipefail

TS=$(command -v tailscale || /usr/bin/tailscale)
HOST=$(hostname -s 2>/dev/null || hostname)

# Step 1: Check Tailscale is connected
if ! "$TS" status 2>&1 | grep -q "$HOST.*online"; then
  sudo -n "$TS" up 2>/dev/null || sudo "$TS" up
  sleep 5
fi

# Step 2: Check funnel route
STATUS=$("$TS" funnel status 2>&1)
if echo "$STATUS" | grep -q "/telegram proxy http://127.0.0.1:8443"; then
  echo "OK: /telegram funnel running"
  exit 0
fi

echo "FUNNEL DOWN -> attempting restart"
OUT=$(sudo -n "$TS" funnel --bg --set-path=/telegram http://127.0.0.1:8443 2>&1)
RC=$?
if [ $RC -eq 0 ] && echo "$OUT" | grep -vq "Access denied"; then
  echo "RESTARTED via sudo: $OUT"
  exit 0
fi
OUT2=$("$TS" funnel --bg --set-path=/telegram http://127.0.0.1:8443 2>&1)
RC2=$?
if [ $RC2 -eq 0 ] && echo "$OUT2" | grep -vq "Access denied"; then
  echo "RESTARTED (no sudo): $OUT2"
  exit 0
fi
echo "COULD NOT RESTART"
exit 1

9.3 Hermes Cron Job — Funnel Watchdog

Runs every 5 minutes via Hermes internal cron system (no system crontab). Configured in /home//.hermes/cron/jobs.json:

{
  "id": "be5f0ae94275",
  "name": "Funnel Watchdog",
  "prompt": "Restart the Tailscale Funnel tunnel if it went down...",
  "no_agent": true,
  "script": "ensure_tailscale_funnel.sh",
  "schedule": { "kind": "interval", "minutes": 5 },
  "enabled": true,
  "deliver": "local"
}
Note on sudo passwords: If the watchdog script cannot run sudo -n tailscale up (requires password), the funnel restart will fail. To fix: sudo tailscale set --operator=$USER allows most tailscale commands without sudo. For tailscale up, configure passwordless sudo for that specific command via visudo.

10. Verification Checklist

After rebuilding, run these checks to confirm everything works:

#CheckCommand
1Hermes respondshermes --version
2WordPress accessiblecurl -s -o /dev/null -w "%{http_code}" http://localhost:8080 → 200
3WooCommerce activedocker exec theblessedhoneybee-wp-wordpress-1 wp plugin list --allow-root
4Tailscale connectedtailscale status | grep "$(hostname).*online"
5Funnel routes activetailscale funnel status
6Telegram gateway listeningss -tlnp | grep 8443
7Webhook registeredcurl -s "https://api.telegram.org/bot$TOKEN/getWebhookInfo" | grep -c "last_error" → 0
8Funnel public reachabilitycurl -X POST https://.ts.net/ → 403 (expected — proves tunnel is live)
9Gateway service runningsystemctl --user is-active hermes-gateway.service
10Watchdog cron scheduledCheck /home//.hermes/cron/jobs.json for Funnel Watchdog
11AgentMail configuredVerify @agentmail.to inbox exists

11. Troubleshooting

SymptomLikely CauseFix
Gateway stuck on "Connecting to Telegram (attempt 1/8)" DNS-over-HTTPS fallback IP discovery hang in WSL Set HERMES_TELEGRAM_DISABLE_FALLBACK_IPS=1 in .env and restart gateway
Gateway exits with code 75 repeatedly Same as above — WSL fallback IP hang Verify HERMES_TELEGRAM_DISABLE_FALLBACK_IPS=1 is present
WordPress returns 500 on first load WordPress still copying files to volume Wait 30s, check docker logs theblessedhoneybee-wp-wordpress-1
Funnel shows "Funnel on" but Telegram not reachable Only root route exists, not /telegram path Add path: tailscale funnel --bg --set-path=/telegram http://127.0.0.1:8443
After WSL reboot, Tailscale offline WSL restart doesn't restart Tailscale sudo tailscale up — or the watchdog handles it
Funnel routes show locally but 404 from internet Edge config desync after Tailscale reconnect sudo tailscale funnel reset then re-apply all routes
Watchdog can't restart funnel sudo password required sudo tailscale set --operator=$USER
Google Workspace auth expired Token revoked or expired Re-run the OAuth setup flow
Can't find the Hermes CLI binary venv not activated source /home//.hermes/hermes-agent/venv/bin/activate or use full path

12. Key File Inventory

These files contain the entire setup. Back them up to preserve the configuration:

File PathPurpose
/home//.hermes/.envAll secrets and environment variables (backup this!)
/home//.hermes/config.yamlHermes agent configuration
/home//.hermes/google_token.jsonGoogle OAuth token (auto-refreshes)
/home//.hermes/google_client_secret.jsonGoogle OAuth client credentials
/home//.hermes/hermes-agent/Hermes agent source code (git clone)
/home//.hermes/scripts/ensure_tailscale_funnel.shFunnel watchdog script
/home//.hermes/cron/jobs.jsonHermes cron job definitions
/home//.config/systemd/user/hermes-gateway.serviceSystemd user service for gateway
/home//theblessedhoneybee-wp/docker-compose.ymlWordPress + MySQL Docker stack
/home//affine/docker-compose.ymlAFFiNE Docker stack
/home//n8n/docker-compose.ymln8n Docker stack
Backup Strategy: The essential files to backup are .env (secrets), config.yaml, the google_*.json files, and all docker-compose.yml files. WordPress database content lives in Docker volumes — back up with docker exec wp db export or docker run --volumes-from.

Generated: 29 September 2026 • Hermes v0.21.0 • Host: .ts.net

This document is designed for full rebuild — follow steps in order from WSL install through verification.

Bottom line
a WSL2 Hermes gateway with Docker WordPress, Tailscale Funnel, and a systemd watchdog gives a reproducible remote-automation host.
📍 Location Tracking
We use your IP address (country/city only) for analytics. No personal data collected.