Files
hermes-skills/skills/devops/infrastructure-automation/references/multi-server-threshold-alerting.md
T

3.5 KiB

Multi-Server Resource Threshold Alerting

Monitors RAM, disk, and CPU usage across all VPS servers, alerting via Telegram when thresholds are crossed. Runs as a Hermes no-agent cron job every 15 minutes.

Architecture

Bash script (vps-threshold-check.sh)
  └─ Server manifest (hostname|ip|local_flag)
      ├─ ssh to each remote server (itpp-infra key, 5s timeout)
      └─ local commands for core server
  ├─ Collect: df -h / (disk %), free (RAM %), top -bn1 (CPU %), nproc
  ├─ Check 3 thresholds (80%, 90%, 95%) per metric per server
  ├─ 24h re-alert suppression via JSON state file
  └─ stdout on alert → no-agent cron → Telegram delivery

Script: /root/.hermes/scripts/vps-threshold-check.sh

Server Manifest Format

"hostname|ip_address|is_local_flag"
  • is_local_flag=1 — run commands directly (core server)
  • is_local_flag=0 — SSH via /root/.ssh/itpp-infra key

Alert State File

/root/.hermes/data/threshold-alerts.json — JSON object keyed by hostname_metric_threshold (e.g. ai_disk_90), value is ISO timestamp of last alert.

Threshold Logic

  • Per threshold: 80%, 90%, and 95% — each tracked independently
  • Alert on first crossing: records timestamp, outputs alert to stdout
  • Suppress re-alerts: 86,400 seconds (24h) cooldown per key
  • Reset on drop: when metric falls below a threshold, that alert key is removed from state — so it can re-alert immediately on the next spike
  • No output when healthy: silent stdout = no Telegram noise (cron skips delivery)

Cron Job

Name:      vps-threshold-check
Schedule:  every 15m
Script:    vps-threshold-check.sh
Mode:      no-agent (script stdout delivered directly)
Deliver:   telegram

Uses Hermes' no-agent cron pattern: script writes alert text to stdout → cron scheduler delivers to Telegram via the running gateway's TELEGRAM_BOT_TOKEN env var.

Threshold Design Decisions

  • 3-tier thresholds rather than a single number: gives escalating urgency (80% ≈ warning, 90% ≈ critical, 95% ≈ emergency)
  • Per-threshold tracking not per-metric: if disk jumps from 70% to 92%, both the 80% and 90% alerts fire once (not every 15 min)
  • 24h re-alert window prevents notification fatigue but ensures admins still hear about sustained issues
  • keyed suppression rather than last-N-hours per server: handles the case where a metric oscillates across a threshold

Known Pitfalls

  • CPU measurement: top -bn1 reads the first sample which may show 0% on idle systems. For accurate CPU under load, use top -bn2 | awk '/Cpu\(s\)/ {print $2}' | tail -1 (second sample picks up between-snapshot delta). The current script uses the first sample which is fine for sustained high-CPU scenarios.
  • SSH timeouts: each server has a 5s ConnectTimeout. If a server is unreachable, the ssh output is empty and metrics default to 0 — no false alarms.
  • Alert state persistence: if the JSON file is deleted, all thresholds re-alert on the next run (resets the 24h clock). This is intentional — better to re-alert than stay silent.
  • Telegram env vars: delivery requires the gateway process to have TELEGRAM_BOT_TOKEN, TELEGRAM_ALLOWED_USERS, and TELEGRAM_HOME_CHANNEL in its environment (set at hermes gateway run launch). These are inherited from the parent shell or .env.
  • Gateway not running: if the gateway is down, the cron scheduler logs failed delivery but the script still runs fine — the JSON state file is updated regardless.