# 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.