Health Check Observability

How often each check runs, how long it takes, what resources it uses, what is exposed in Prometheus/Grafana, and what happens automatically on failure.


1. Check Duration & Resource Budget

health-check.yml — every 2 hours (GH Actions)

Runs as two parallel jobs on separate runners.

JobRunnerStepsTypical durationGH Actions minutes
health-httphstgr (self-hosted, vps-h1)5 curl checks + backstop + report~1–2 minfree
health-runnersionos (self-hosted, vps-i1)3 SSH checks + report~1 minfree

Total per run: ~2 min elapsed, 0 billed minutes (both jobs on free self-hosted runners). Per day: 12 runs × 0 billed minutes = free.

Resource cost per step:

  • curl HTTP probe: negligible — single HTTPS request, <1 s
  • WAHA: two requests (server status + session) — <2 s
  • SSH systemctl is-active: <3 s per runner (SSH handshake + command)
  • GH issue create/update: single API call — <1 s

nightly-infra-check.yml — 00:30 UTC daily (GH Actions, ionos runner)

Single job, Claude agent, ~25–45 min depending on server response times.

PhaseWhatTypical time
Import Infisical secretsREMOVED (Infisical CE decommissioned 2026-06-21)N/A
Phase 1 — Docker (5 servers)5× SSH + docker ps~20–40 s
Phase 2 — MongoDB rs01× SSH + mongosh rs.status()~10–15 s
Phase 3 — Disk (6 servers)6× SSH + df -h~20–40 s
Phase 4 — Prometheus targets1× HTTPS REST~3 s
Phase 5 — Prometheus alerts1× HTTPS REST~2 s
Phase 6 — n8n executions1× HTTPS REST~3 s
Phase 7 — Wasabi backup freshness1× PromQL query~3 s
Phase 8 — Infisical CEREMOVED (decommissioned 2026-06-21)N/A
Phase 9 — Supabase compliance1× REST query~3 s
Phase 10 — Audit action failures1× REST query~3 s
Phase 11 — Report + GH issue(s)gh CLI calls~5–30 s per issue
Claude agent overheadLLM inference per phase~15–30 s
Total~3–8 min clean; ~10–20 min with issues

Resource cost: Uses the ionos runner (free). The Claude agent runs as the IONOS runner process — no separate VPS cost. SSH connections are stateless and closed immediately after each command.

hourly-devops-triage — every hour (bms-4 Linux cron)

Claude agent, typically 2–5 min.

ActivityTime
Fetch triage issues (gh CLI)~3 s
SLA watchdog (gh CLI per stale issue)~2 s each
Per issue: classify + act~5–15 s (Tier 1 SSH fix: +10 s)
Tier 2 SSH spawn~5 s (fire-and-forget)
Telegram API call~1 s
Total (0 issues)~10 s
Total (5 issues, mixed)~1–2 min

Resource cost: Runs on bms-4 as claude-runner. Uses Claude Max OAuth — no API credit cost.


2. Prometheus & Grafana Exposure

What is currently scraped by Prometheus

Prometheus on vps-i1 scrapes the following targets relevant to health checks:

TargetJobPortMetrics
vps-i1 hostnode_exporter:9100CPU, RAM, disk, network, load
vps-h1 hostnode_exporter:9100same
vps-i1 containerscadvisor:8080Per-container CPU/RAM/restart count
vps-h1 containerscadvisor:8080same
Supabase queue depthsqueue_exporter:9200p24_queue_depth{queue}
Supabase slow queriespg_stats_exporter:9201p24_slow_query_count
Wasabi backup freshnessbackup_exporter:9220backup_age_seconds{backup_name}
Vercel/Supabase costscost_exporter:9210p24_cost_usd{service}
Credential rotation agecredential_exporter:9230p24_credential_days_overdue{credential}
HTTP probesblackbox_exporter:9115probe_success, probe_duration_seconds

What health-check results are NOT yet in Prometheus

The health-check.yml and nightly-infra-check results are GitHub issue-native only — they are not pushed to Prometheus. This is a gap.

CheckIn Prometheus?Gap
WAHA session statusPartial — blackbox probes HTTP, not session stateSession WORKING/FAILED not exposed
Grafana HTTPYes — blackbox probe
n8n HTTPYes — blackbox probe
Traccar HTTPYes — blackbox probe
MongoDB rs0 PRIMARY countNoNot scraped — nightly check only
GH runner statusNoSSH check only, not a metric
Disk usage (bms-1, bms-2, bms-3)Nonode_exporter only on vps-i1 and vps-h1
n8n execution error countNonightly check only
Nightly check pass/failNoGH issue only
Hourly triage: issues processedNostdout only

Grafana dashboards

DashboardWhat it showsDatasource
p24-infra HealthCustom exporters: backup age, cost, queue depth, credential rotationThanos Query (Prometheus)
Node Exporter FullHost CPU/RAM/disk/network for vps-i1 and vps-h1Thanos Query
cAdvisorPer-container resource usageThanos Query
BlackboxHTTP probe success/latency per endpointThanos Query

Currently no Grafana dashboard shows: MongoDB rs0 state, GH runner status, nightly check history, hourly triage activity.


3. What Happens on Failure — Automated Reaction Matrix

health-check.yml failure (every 2h)

health-http or health-runners job FAILS
         │
         ▼
health-report job (always runs)
         │
         ├── Discord embed: "❌ service1 service2 failed" (every run while failing)
         │
         └── GitHub issue:
               • First failure → create issue labeled server-down,triage,infra-check-fail
               • Repeat failures → comment "Still failing: …" on existing issue
               • Recovery → close issue, add label resolved

The server-down issue has label p24-infra-nc-alert — picked up by hourly triage within 1 hour.

Triage reaction:

  • HTTP failures (Traccar, Grafana, n8n, WAHA) → match HUMAN_REQUIRED_PATTERNS or escalate as unknown → Tier 3: Telegram to human
  • WAHA session FAILED specifically → HUMAN_REQUIRED_PATTERNS (“WAHA”) → Tier 3 immediately
  • Runner down → Tier 3 immediately (production restart pattern)

nightly-infra-check.yml failure (00:30 UTC)

Nightly agent runs all 11 phases
         │
         ├── Each problem found → create GH issue labeled p24-infra-nc-alert
         │     P1-Critical → also: label human-action added by triage within 1h
         │     P2-Warning  → triage within 1h: Tier 1, 2, or 3
         │     P3-Info     → labeled patch, queued for AI-Dev or human
         │
         └── Agent itself crashes → Discord embed (red) + meta GH issue

Hourly triage reaction per tier

TierTriggerAutomated actionHuman notified?
Tier 1 — Auto-fixDocker container Exited/RestartingSSH docker restart, verify, move issue to In ProgressNo
Tier 1 — Auto-fixPrometheus config reload neededcurl -X POST /-/reload, verify HTTP 200No
Tier 1 — Auto-fixDocs compliance gapLabel patch, add commentNo (Tier 2 may spawn)
Tier 1 — Auto-fixCredential rotation (auto_rotate=true)Trigger credential-rotation.ymlNo
Tier 2 — AI-DevDocs compliance missing workbookSSH spawn worker-issue on AI-Dev-IO1No (SLA watchdog at 4h)
Tier 2 — AI-DevAudit action failedSSH spawn worker on AI-Dev-IO1No
Tier 2 — AI-DevGH Actions workflow failedSSH spawn worker on AI-Dev-IO1No
Tier 2 — AI-DevPrometheus target DOWNSSH spawn worker on AI-Dev-IO1No
Tier 2 SLA watchdogai-dev-queued > 4h, no PRRelabel human-actionYes — Telegram P2
Tier 3 — HumanMongoDB, OOM, disk ≥90%, P1Label human-actionYes — Telegram
Tier 3 — HumanCredential rotation (auto_rotate=false)Label human-actionYes — Telegram P2
Tier 3 — HumanProduction service restartLabel human-actionYes — Telegram
Tier 3 — HumanUnknown patternLabel human-actionYes — Telegram P3

Prometheus Alertmanager reaction (continuous)

Alertmanager fires independent of the above. Overlap is intentional — belt-and-braces.

AlertRoutingAction
EndpointDown (any HTTP probe)P1 → immediate emailAlso triggers alertmanager-escalation.yml → GH issue [infra-alert] → hourly triage
HighCPUP2 → daily digest emailGH issue
CredentialRotationCriticalP1 → immediate emailGH issue per credential
CredentialRotationOverdueP2 → daily digestGH issue
PromtailNotSendingLogsP2 → daily digestGH issue
BackupExporterDownSuppressed (known flap)Only fires after 1h

4. Full Failure → Resolution Flow

Problem occurs (e.g. WAHA session drops at 03:00 UTC)
         │
         ├── [Continuous] Prometheus blackbox probe fails → Alertmanager fires
         │   → email to radieu@gmail.com (P1 immediate)
         │   → alertmanager-escalation.yml → [infra-alert] GH issue
         │
         ├── [Every 2h] health-check.yml detects WAHA FAIL
         │   → Discord embed ❌
         │   → Creates/updates server-down GH issue with label p24-infra-nc-alert
         │
         ├── [Next hour] hourly-devops-triage picks up p24-infra-nc-alert issue
         │   → WAHA matches HUMAN_REQUIRED_PATTERNS
         │   → Labels human-action
         │   → Sends Telegram (P1 bypasses quiet hours → fires at 03:xx Warsaw)
         │
         └── Human resolves → WAHA session recovers
             → next health-check.yml run: all green → closes server-down issue
             → Discord: "All services healthy again"

GapEffortValueIssue
Push nightly check pass/fail as Prometheus gaugeLowGrafana history of check healthNew
Expose WAHA session status as Prometheus metric (custom exporter)MediumReal-time session state in GrafanaNew
Add node_exporter to bms-2, bms-3, bms-4MediumDisk/CPU alerts for all serversNew
Grafana dashboard: nightly check timeline + triage activityMediumSingle-pane health viewNew
Transition-only Discord for health-check (not every-run)LowReduce noisespec 06
bms-4 runner health check in health-check.ymlLowDetect AI-Dev runner downNew