Agent Host — First Install & Hardening
Complete procedure for provisioning a fresh Hostinger VPS KVM2 running Ubuntu 24.04 LTS to run the Hermes Agent with security hardening.
Prerequisites:
- Hostinger account with ability to provision VPS
- SSH key pair generated on your local machine (
~/.ssh/hostinger_vps_ed25519)- Cloudflare account with the domain registered
- 30–45 minutes
Phase 1: Pre-Installation (Local Machine)
Section titled “Phase 1: Pre-Installation (Local Machine)”1.1 Generate SSH key
Section titled “1.1 Generate SSH key”On your Mac:
ssh-keygen -t ed25519 -C "francois@ducatillon.net-hostinger-vps" -f ~/.ssh/hostinger_vps_ed25519# Press Enter twice (no passphrase)This creates:
- Private key (
~/.ssh/hostinger_vps_ed25519) — for your Mac - Public key (
~/.ssh/hostinger_vps_ed25519.pub) — for Hostinger
Backup private key to Credential Vault (Bitwarden).
1.2 Add to SSH config
Section titled “1.2 Add to SSH config”Edit ~/.ssh/config:
Host hostinger-vps HostName <YOUR_VPS_IP> User hermes IdentityFile ~/.ssh/hostinger_vps_ed25519Replace <YOUR_VPS_IP> with your actual VPS IP (you’ll get this from Hostinger after provisioning).
Phase 2: OS Installation (Hostinger Dashboard)
Section titled “Phase 2: OS Installation (Hostinger Dashboard)”2.1 Provision VPS
Section titled “2.1 Provision VPS”-
Log in to Hostinger dashboard
-
Create new VPS → select Ubuntu 24.04 LTS
-
Set root password (required by Hostinger, even though we’ll disable it)
-
Paste SSH public key in the OS installation wizard:
Terminal window cat ~/.ssh/hostinger_vps_ed25519.pub # Copy entire outputPaste into Hostinger’s “SSH Key” field
-
Complete installation → note your VPS IP address
2.2 Update SSH config
Section titled “2.2 Update SSH config”Edit ~/.ssh/config with the actual VPS IP you received.
Phase 3: System Hardening (VPS Console)
Section titled “Phase 3: System Hardening (VPS Console)”SSH into VPS as root:
ssh -i ~/.ssh/hostinger_vps_ed25519 root@<YOUR_VPS_IP>3.1 Update system
Section titled “3.1 Update system”apt update && apt upgrade -yTakes 1–2 minutes. If prompted about restarting services, press Enter to accept defaults.
3.2 Create hermes user
Section titled “3.2 Create hermes user”useradd -m -s /bin/bash hermes3.3 Copy SSH key to hermes user
Section titled “3.3 Copy SSH key to hermes user”mkdir -p /home/hermes/.sshcp /root/.ssh/authorized_keys /home/hermes/.ssh/authorized_keyschmod 700 /home/hermes/.sshchmod 600 /home/hermes/.ssh/authorized_keyschown -R hermes:hermes /home/hermes/.ssh3.4 Allow hermes to sudo without password
Section titled “3.4 Allow hermes to sudo without password”echo "hermes ALL=(ALL) NOPASSWD: ALL" | tee /etc/sudoers.d/hermes3.5 Harden SSH
Section titled “3.5 Harden SSH”Disable root login and password auth:
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_configsed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_configsshd -t && echo "Config OK" || echo "CONFIG ERROR"systemctl restart ssh✅ Should see “Config OK”.
3.6 Firewall (UFW)
Section titled “3.6 Firewall (UFW)”ufw allow 22/tcpufw --force enableufw statusExpected output:
Status: active
To Action From-- ------ ----22/tcp ALLOW Anywhere22/tcp (v6) ALLOW Anywhere (v6)3.7 Automatic security updates
Section titled “3.7 Automatic security updates”apt install -y unattended-upgradesdpkg-reconfigure -plow unattended-upgradesSelect “Yes” when prompted.
3.8 Reboot
Section titled “3.8 Reboot”sudo rebootVPS will disconnect. Wait 30 seconds.
3.9 Verify hermes access
Section titled “3.9 Verify hermes access”Open a new terminal and test:
ssh -i ~/.ssh/hostinger_vps_ed25519 hermes@<YOUR_VPS_IP>✅ Should connect without password.
Do NOT proceed if root still accepts SSH. Verify SSH hardening worked before continuing.
Phase 4: Cloudflare Tunnel Setup
Section titled “Phase 4: Cloudflare Tunnel Setup”Why a Tunnel? (Security & Architecture Reasoning)
Section titled “Why a Tunnel? (Security & Architecture Reasoning)”The problem without Tunnel: A VPS has a public IP address in a datacenter. If port 9119 is opened directly to the internet, the dashboard becomes accessible to anyone who knows the IP + port. Even with authentication (OTP), the attack surface is much larger.
What a Tunnel does:
- VPS initiates an outbound connection to Cloudflare edge (inbound connections are not accepted)
- Cloudflare acts as a proxy — it receives internet traffic and routes it through the tunnel back to the VPS
- The firewall remains restrictive, allowing only SSH (port 22) inbound — all other ports are blocked
- Tunnel traffic is encrypted — anyone sniffing the wire sees encrypted tunnel traffic, not plaintext HTTP
Why this matters:
- Zero inbound exposure — VPS firewall stays closed (UFW blocks everything except SSH)
- Cloudflare’s CDN & DDoS protection — malicious traffic is absorbed before reaching the VPS
- Email OTP authentication — Cloudflare Access gates the dashboard; even if someone discovers the URL, authentication is required
- Portable infrastructure — if the VPS provider changes, the tunnel simply points to the new IP
Real-world analogy:
- Without tunnel: VPS door is open to the street. Anyone can attempt to enter.
- With tunnel: VPS door stays locked. Cloudflare operates a reception desk on the street that verifies credentials before allowing entry.
4.1 Create tunnel in Cloudflare dashboard
Section titled “4.1 Create tunnel in Cloudflare dashboard”- https://dash.cloudflare.com/ → select the domain → Tunnels (left sidebar)
- Click Create a tunnel
- Type: Cloudflared
- OS: Debian
- Architecture: 64-bit
- Name:
hermes-<provider>(e.g.,hermes-hostinger) - Save
4.2 Install cloudflared on VPS
Section titled “4.2 Install cloudflared on VPS”On VPS (as hermes user):
sudo mkdir -p --mode=0755 /usr/share/keyringscurl -fsSL https://pkg.cloudflare.com/cloudflare-public-v2.gpg | sudo tee /usr/share/keyrings/cloudflare-public-v2.gpg >/dev/nullecho 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.listsudo apt-get update && sudo apt-get install cloudflared4.3 Install tunnel service
Section titled “4.3 Install tunnel service”From Cloudflare dashboard, copy the Debian/64-bit service install command:
sudo cloudflared service install <TOKEN>(Replace <TOKEN> with the actual token from the dashboard.)
4.4 Start tunnel
Section titled “4.4 Start tunnel”sudo systemctl start cloudflaredsudo systemctl status cloudflared✅ Should show active (running) with tunnel connections established.
4.5 Configure public route
Section titled “4.5 Configure public route”In Cloudflare dashboard → your tunnel → Add a route → Published application:
| Field | Value |
|---|---|
| Subdomain | hermes |
| Domain | Your domain (e.g., ducatillon.net) |
| Path | (leave empty) |
| Type | HTTP |
| Service URL | http://localhost:9119 |
Save. DNS CNAME created automatically.
Phase 5: Verification
Section titled “Phase 5: Verification”✅ VPS ready for Hermes Agent installation
Check:
- SSH access as
hermes(key-based) ✅ - Root SSH disabled ✅
- UFW firewall active (port 22 only) ✅
- cloudflared running ✅
- Tunnel route configured ✅
Phase 5: Prepare Credentials (Local Machine)
Section titled “Phase 5: Prepare Credentials (Local Machine)”Before SSH into the VPS, gather the secrets you’ll need for Hermes Agent configuration.
5.1 Create Discord bot
Section titled “5.1 Create Discord bot”-
Click New Application → name:
Hermes -
Left sidebar → Bot section → Add Bot
-
Copy the Token → save to Bitwarden immediately (visible only once)
-
Disable “Public Bot” if you want it private (optional)
-
Bot permissions needed:
- ✅
Send Messages - ✅
Read Message History
- ✅
-
Create a personal Discord server for testing (if you don’t have one):
- Discord app → Add a server → Create My Own → name it
Hermes Lab - Later, you’ll invite the bot to this server
- Discord app → Add a server → Create My Own → name it
You now have: DISCORD_BOT_TOKEN (saved in Credential Vault)
5.2 Create OpenRouter API key
Section titled “5.2 Create OpenRouter API key”- Go to https://openrouter.ai
- Sign up with email (free tier)
- Dashboard → API Keys (left sidebar)
- Click Create New Key or Create API Key
- Name it:
hermes-agent-openrouter-key - Optional: Set credit limit (recommended: €15/month to prevent surprise bills)
- Copy the API Key → save to Bitwarden immediately (visible only once)
You now have: OPENROUTER_API_KEY (saved in Credential Vault)
Both secrets are now ready for Phase 6 (Hermes Agent install).
Phase 6: Hermes Agent Installation
Section titled “Phase 6: Hermes Agent Installation”Prerequisites
Section titled “Prerequisites”Before starting, you need:
- Discord bot token — from Developer Portal (saved in Credential Vault)
- OpenRouter API key — from openrouter.ai (saved in Credential Vault)
- Google Personal OAuth2 refresh token — from Google Cloud Console (roadmap for Drive Bridge; can skip day one if using web-only)
6.1 Install Hermes Agent
Section titled “6.1 Install Hermes Agent”SSH into VPS as hermes:
ssh hostinger-vpsInstall via the official curl installer:
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bashThis:
- Installs Python 3.11, uv, Node.js, ripgrep, ffmpeg
- Creates
~/.hermes/directory structure - Installs Hermes Agent to
~/.local/bin/hermes
When done, reload your shell:
source ~/.bashrcVerify installation:
hermes --version✅ Should show version number (e.g., hermes-agent 0.14.0).
6.2 Create .env configuration
Section titled “6.2 Create .env configuration”Create ~/.hermes/.env with your secrets:
cat > ~/.hermes/.env << 'EOF'# Discord GatewayDISCORD_BOT_TOKEN=<YOUR_DISCORD_BOT_TOKEN>
# LLM ProviderOPENROUTER_API_KEY=<YOUR_OPENROUTER_API_KEY>
# Optional: Google Drive (Drive Bridge — roadmap)# GOOGLE_OAUTH2_REFRESH_TOKEN=<YOUR_GOOGLE_REFRESH_TOKEN>
# Optional: GitHub (for code projects — roadmap)# GITHUB_PAT=<YOUR_GITHUB_PAT>EOFReplace <YOUR_DISCORD_BOT_TOKEN> and <YOUR_OPENROUTER_API_KEY> with the values from Bitwarden.
Secure the file:
chmod 600 ~/.hermes/.env6.3 Choose your LLM provider
Section titled “6.3 Choose your LLM provider”Hermes supports 200+ models via OpenRouter. Choose a default:
hermes modelFollow the prompts to select:
- Provider: OpenRouter
- Model: Start with a free model (e.g., Llama 3.1 8B via MiMo) for testing
6.4 Configure Discord gateway
Section titled “6.4 Configure Discord gateway”Start the setup wizard:
hermes setupWhen prompted, select:
- Gateway: Discord
- Discord Token: (will prompt you to paste)
- Model: Your choice from step 6.3
The wizard creates ~/.hermes/config.yaml with all settings.
6.5 Create .hermes/SOUL.md
Section titled “6.5 Create .hermes/SOUL.md”Copy your agent identity context:
cat > ~/.hermes/SOUL.md << 'EOF'# [Paste the SOUL.md draft from ../soul-draft.md here]EOF(See SOUL.md draft template — copy the entire content.)
6.6 Create .hermes/CONTEXT.md
Section titled “6.6 Create .hermes/CONTEXT.md”Link to your architecture glossary:
cat > ~/.hermes/CONTEXT.md << 'EOF'# [Paste the CONTEXT.md from ../CONTEXT.md here]EOF(See CONTEXT.md glossary — this gives Hermes the domain language.)
6.7 Create systemd service (optional, for auto-restart)
Section titled “6.7 Create systemd service (optional, for auto-restart)”For production, run Hermes as a systemd user service:
cat > ~/.config/systemd/user/hermes-gateway.service << 'EOF'[Unit]Description=Hermes Agent GatewayAfter=network.target
[Service]Type=simpleExecStart=%h/.local/bin/hermes gatewayRestart=on-failureRestartSec=10
[Install]WantedBy=default.targetEOF
systemctl --user daemon-reloadsystemctl --user enable hermes-gatewaysystemctl --user start hermes-gatewayVerify:
systemctl --user status hermes-gateway6.8 Test the gateway
Section titled “6.8 Test the gateway”Send a test message via Discord:
- Open Discord app → find “Hermes Lab” server → bot should be there
- Send message:
@Hermes hello - Check response in Discord (should reply)
✅ Gateway is working.
6.9 Test CLI access
Section titled “6.9 Test CLI access”On the VPS, try the interactive CLI:
hermes --tuiType a question (e.g., What is my purpose?) → press Enter.
✅ Hermes should respond using your selected model.
Type /quit to exit.
Phase 7: Dashboard & Monitoring
Section titled “Phase 7: Dashboard & Monitoring”7.1 Dashboard access
Section titled “7.1 Dashboard access”The web dashboard should now be available at:
https://hermes.ducatillon.net(Protected by Cloudflare Access — you’ll see an OTP prompt.)
✅ You should see:
- Session history
- Logs
- Chat interface
- Analytics
- Cron jobs
7.2 Health monitoring (Uptime Kuma)
Section titled “7.2 Health monitoring (Uptime Kuma)”Set up optional Uptime Kuma for monitoring Hermes services:
# Roadmap: Uptime Kuma setup# For now, manually check service status:systemctl --user status hermes-gatewayNext: Hermes Agent Configuration & Automation (roadmap)
Post-Install Verification
Section titled “Post-Install Verification”✅ Hermes Agent is live
Checklist:
-
hermes --versionshows version number -
~/.hermes/.envconfigured with Discord + OpenRouter -
hermes --tuiresponds to questions - Discord bot receives messages and replies
- Web dashboard accessible at hermes.ducatillon.net
-
systemctl --user status hermes-gatewayshows running (if using systemd)
Troubleshooting
Section titled “Troubleshooting”| Issue | Diagnosis | Fix |
|---|---|---|
| Install script fails | Python 3.11 not available | Run: apt install -y python3.11 python3.11-venv |
hermes: command not found | PATH not updated | Run: source ~/.bashrc and try again |
| Discord bot doesn’t reply | Token missing or invalid | Check ~/.hermes/.env has correct DISCORD_BOT_TOKEN |
| OpenRouter API errors | Key invalid or expired | Regenerate key at openrouter.ai and update ~/.hermes/.env |
| Dashboard not accessible | Tunnel not routing to port 9119 | Verify in Cloudflare dashboard: route points to http://localhost:9119 |
| Systemd service fails | Config not readable | Run: journalctl --user -u hermes-gateway -n 50 to see errors |
Next: Install Hermes Agent
Troubleshooting
Section titled “Troubleshooting”| Issue | Diagnosis | Fix |
|---|---|---|
| SSH key auth not working | Public key not in hermes’s authorized_keys | Verify: cat /home/hermes/.ssh/authorized_keys contains your public key |
| Root SSH still works | SSH config not restarted | Run: systemctl restart ssh |
| UFW blocks connections | Rule syntax error | Run: ufw status verbose and verify port 22 is ALLOW |
| Tunnel not connecting | Cloudflare token missing/expired | Re-run service install command from dashboard |
| Tunnel connects but dashboard unreachable | Hermes not listening on 9119 yet | Normal — will be resolved after Hermes agent install |
Security checklist
Section titled “Security checklist”- SSH key stored in Credential Vault
- Root password disabled or randomized
- Password auth disabled (SSH key only)
- UFW firewall enabled (port 22 only)
- Unattended-upgrades configured
- Cloudflare Tunnel connected
- Hermes user can sudo without password