Alerting Flow — Centralizado via Alertmanager¶
Last updated: 2026-08-01 (reescrito tras el rediseño "silencio primero" del 2026-07-20 — la versión anterior de este documento, de 2026-07-12, describía un diseño ya sustituido)
El componente central de alertas del homelab es Prometheus Alertmanager corriendo en VM 208 (192.168.0.208). Recibe alertas de Prometheus (reglas + Grafana) y las enruta con política "silencio primero": por defecto todo va solo a Vera (webhook, silenciosa, con digest 6h); solo un carril reducido de alertas realmente graves interrumpe con Telegram al instante. El viejo flujo basado en n8n queda como legacy (ver más abajo).
🎯 Diseño (actual, "silencio primero" desde 2026-07-20)¶
┌────────────────────────┐
│ Prometheus rules │ ─┐
│ + Grafana alert rules │ │
└────────────────────────┘ │
▼
┌───────────────────────┐
│ Alertmanager │ (VM 208, prom/alertmanager)
│ /home/monxas/appdata/ │
│ alertmanager/ │
│ alertmanager.yml │
│ │
│ receiver por defecto: VERA (silencioso)
│ │
│ Carril 1 — GRAVE (14 alertnames: corrupción,
│ RAID/SMART/NVMe, disco crítico, NAS/Caddy caídos)
│ → telegram-grave AL INSTANTE + continue:true → Vera
│ también, en paralelo (repeat 4h)
│ │
│ Carril 2 — TODO LO DEMÁS (severity critical|warning)
│ → SOLO Vera, en silencio (repeat 24h; el bridge
│ aplica su propio cooldown 30min por fingerprint)
│ │
│ inhibit_rules: cascada NAS, jerarquía de disco,
│ bundling SMART, critical ⊳ warning genérico
└───────┬────────────┬───┘
│ │
┌───────────▼──┐ ┌─────▼─────────────┐
│ telegram-grave│ │ vera (webhook) │
│ chat_id │ │ http://vera.lan:8099/alert
│ 730947207 │ │ Bearer auth │
│ parse_mode HTML│ │ send_resolved │
└──────┬────────┘ └────────┬──────────┘
▼ ▼
api.telegram.org Vera diagnostica + intenta
Bot @Veraclawd_bot arreglo reversible. Si falla o
Chat 730947207 descubre algo grave, rompe el
silencio; si no, digest cada 6h.
┌────────────────────────┐
│ Proxmox (pmx-50/51) │ ──► webhook `telegram` (PVE notifications)
│ PVE notifications.cfg │ → api.telegram.org (chat 730947207)
│ SOLO severity warning/error va a Telegram; TODO va a mail root@pam │
└────────────────────────┘
┌────────────────────────┐
│ backup-freshness.sh │ ──► Telegram vía bot de Vera en CT100
│ (pmx-50, cron 09:00) │
└────────────────────────┘
Por qué cambió (2026-07-20): antes, toda alerta
critical|warningiba a Telegram Y a Vera en paralelo, así que una sola cascada (p.ej. NAS nfsd colgado → contenedores de media → probes) producía ~25 mensajes redundantes. Ahora solo 14 alertnames realmente graves rompen el silencio de inmediato; el resto lo gestiona Vera callada, con digest cada 6h, y solo avisa si el arreglo automático falla. Detalle del bridge:/root/clawd/scripts/alert-bridge.pyen LXC 100 (Vera, ahora en192.168.0.203).
⚙️ Alertmanager — configuración¶
Fichero: /home/monxas/appdata/alertmanager/alertmanager.yml (container alertmanager). Fuente versionada: ansible/roles/observability_collector/files/alertmanager.yml.
- Route raíz:
receiver: vera(default silencioso),group_by: [alertname, severity],group_wait: 30s,group_interval: 5m,repeat_interval: 12h. - Sub-route Carril 1 (grave): matchea 14
alertnames de riesgo real de datos/caída total (nas_btrfs_corruption,nas_mdadm_degraded,nas_smart_failed,nas_nvme_critical_warning,CriticalDiskUsage,pbs_datastore_main_critical,nas_down,CaddyLXCDown, etc.) → receivertelegram-grave,continue: true(así el carril 2 también dispara a Vera en paralelo),group_wait: 20s,group_interval: 10m,repeat_interval: 4h. - Sub-route Carril 2 (resto):
severity=~"critical|warning"→ receiververaúnicamente,group_wait: 45s,group_interval: 15m,repeat_interval: 24h. - inhibit_rules (más elaboradas que antes): cascada NAS (
nas_down/nas_high_loadsilencia síntomas dependientes sinequal:, porque la causa está en otro host), jerarquía de disco (CriticalDiskUsage⊳HighDiskUsage,nas_volume1_critical⊳otras), bundling de SMART (un disco muriéndose no manda 4 alertas sueltas), NVMe wearout/spare, y la regla genéricaseverity=critical⊳severity=warning(equal: [alertname, instance]).
Receiver telegram-grave (nativo)¶
telegram_configs nativo de Alertmanager (no pasa por n8n):
- bot_token_file: /etc/alertmanager/telegram-bot-token
- chat_id: 730947207
- parse_mode: HTML, send_resolved: true
- Plantilla message: 🔥 GRAVE — {alertname} (firing) / ✅ Resuelto — {alertname} (resolved), con severity + lista de alerts (summary/description) + instance, y nota de que Vera está diagnosticando en paralelo.
Receiver vera (webhook)¶
url: http://vera.lan:8099/alert(LXC 100,192.168.0.203)send_resolved: true,max_alerts: 0- Auth
Bearervíacredentials_file: /etc/alertmanager/vera-alert-secret(fuera de git a propósito).
🎨 Output Telegram¶
Corregido 2026-08-01: el bloque de ejemplo de la versión anterior de este documento
(🚨 alertname / Severity / Status / • summary) correspondía al receiver genérico telegram
que ya no existe. El receiver real hoy es telegram-grave (solo para el carril GRAVE de 14
alertnames), con esta plantilla:
🔥 <b>GRAVE — alertname</b>
Severity: critical
• summary de la alerta
Instance: host:port
🤖 Vera está diagnosticando en paralelo.
En resolución (✅ Resuelto — alertname) no lleva la línea de Vera. Aparte, PVE manda sus
propios mensajes directamente (no vía Alertmanager) con formato 🖥️ *title*\n\nmessage en
Markdown — ver sección PVE → Telegram abajo.
🔔 PVE → Telegram¶
Proxmox notifica directamente, sin pasar por Alertmanager. Configurado en /etc/pve/notifications.cfg (verificado en vivo 2026-08-01 — la descripción de este documento a fecha 2026-07-12 estaba desactualizada, ver nota abajo):
- Endpoint
webhook: telegram→POST https://api.telegram.org/bot{{ secrets.token }}/sendMessage, body conchat_id 730947207,parse_mode Markdown. matcher: default-matcher(modeall) → targetmail-to-rootúnicamente. TODA notificación PVE (incluidos fallos de backup víamailnotification=failure) llega al mail de root.matcher: telegram-important(separado,match-severity warning,error, modeall) → targettelegram. Solo las notificaciones PVE de severitywarning/errorllegan también a Telegram — no todas, como decía la versión anterior de este doc. Los backups OK van solo por mail; el agregado diario se ve en el morning report.
🛡️ backup-freshness.sh (nuevo, 2026-07-12)¶
Monitor de frescura de backups en pmx (/usr/local/sbin/backup-freshness.sh en proxmox-50), cron diario 09:00 (tras las ventanas de backup). Signal-only:
- Para cada guest con job de backup, busca la última snapshot en PBS (
pvesm list pbs). - Umbral 48h (
THRESH_H): si un guest no tiene backup más reciente que eso, o no tiene ninguno, genera aviso. - Críticos (🔴): 100 (Vera), 208 (media), 270/271 (caddy), 280 (rp). El resto son 🟡.
- Entrega la alerta a Telegram vía el bot de Vera dentro de CT100 (
pct exec 100 -- ... source secrets.env ... curl api.telegram.org), sin copiar el token a pmx. - Log en
/var/log/backup-freshness.log.
🚨 Reglas de alerta activas¶
Las reglas viven en Prometheus/Grafana y disparan hacia Alertmanager. Grupos:
Reparto de responsabilidades (desde 2026-07-26)
Los dos pipelines tenían 9 reglas duplicadas, así que el mismo evento llegaba dos veces a
Telegram con nombres distintos (memory-high y HighMemoryUsage, load-high y
HighLoadAverage…). Se deduplicó con un criterio de capacidad, no de preferencia:
| Pipeline | De qué se ocupa | Nº reglas |
|---|---|---|
Prometheus (alerts.yml) |
Umbrales sobre métricas: host, NAS, SMART/NVMe, backup, servicios | 41 |
| Grafana | Lo que Prometheus no puede: logs de Loki, probes sintéticos, métricas de Caddy, tendencias | 20 |
Se borraron de Grafana las 9 duplicadas (backup en
appdata/grafana/alert-rules-backup-20260726.json). Antes de borrar se comparó expresión a
expresión, no sólo el nombre — y ahí apareció la excepción de abajo.
prom-target-down NO es un duplicado de ContainerDown
Se parecen por el nombre y no hacen lo mismo:
prom-target-down→up{job=~"node|proxmox-nodes|cadvisor"} < 1— salta si cae un solo target.ContainerDown→sum by (job) (up{job=~"cadvisor|node"}) == 0— salta sólo si caen todos los de un job, y no cubreproxmox-nodes.
Borrar la de Grafana habría dejado ciego el caso "se muere el node_exporter de pmx-51 o del
NAS". Se conserva a propósito. Misma lógica al eliminar smart-failed (Grafana, for: 0s):
para no perder inmediatez ante un disco muriéndose, nas_smart_failed bajó de 5m a 1m.
Grafana escucha en :3030, no en :3000
En :3000 hay otro contenedor que responde
{"success":false,"error":"Authentication not configured"}. Parece un fallo de credenciales
de Grafana y no lo es — estás llamando al servicio equivocado.
Para editar reglas: PUT|DELETE /api/v1/provisioning/alert-rules/{uid} con cabecera
X-Disable-Provenance: true.
Grafana — homelab-critical (8)¶
| UID | Trigger |
|---|---|
backup-failed |
count_over_time({job="vzdump"} \|~ "ERROR:"[10m]) > 0 |
caddy-upstream-down |
min(caddy_reverse_proxy_upstreams_healthy) < 1 (2min) |
host-silent |
count_over_time({host=~"proxmox-50\|proxmox2-51\|ubuntu-media-server"}[10m]) < 1 |
n8n-down-direct |
n8n DOWN (alerta directa) |
pbs-verify-failed |
PBS verify-job fallido |
pihole-down |
up{job="pihole"} < 1 (3min) |
prom-target-down |
up{job=~"node\|proxmox-nodes\|cadvisor"} < 1 (3min) — cualquier target |
tv-gw-down |
tv-gw LXC DOWN — TV LG sin internet |
Grafana — homelab-warning (8)¶
| UID | Trigger |
|---|---|
caddy-5xx-spike |
rate(caddy_http_requests_total{code=~"5.."}[5m]) > 0.5 (5min) |
caddy-p99-slow |
p99 latency > 2s (10min) |
container-restarting |
container reinicia >3×/15min |
disk-fill-trend |
disco llenándose en <7d (predictiva) |
error-spike |
rate errores reales >2/s (2min) |
loki-grafana-internal-error |
error interno Loki/Grafana |
probe-down |
probe_success < 1 (3min) |
pvesr-fail |
max(pvesr_fail_count) > 0 (15min) — replicación PVE entre nodos |
Grafana — iarq (4)¶
Stack de iarquitectos en la VM de Carmelo: CPU sostenida y disco de openclaw-vm, errores de
iarq y fallos de scraping del RAG. UIDs autogenerados, sin nombre legible.
Prometheus — alerts.yml (52 en 7 grupos, verificado en vivo 2026-08-01)¶
Reagrupado el 2026-07-26 por dominio real. Antes había un grupo nas-smart de 18 reglas que
contenía media_container_missing/_unhealthy (contenedores del stack de media, nada que ver
con SMART) y un nas-preventive que separaba BTRFS/mdadm/PCIe del resto de almacenamiento sin
criterio. Desde entonces se añadió un grupo nuevo, red_network (11 reglas), que no estaba
documentado en la versión anterior de este doc.
| Grupo | Nº | Contenido |
|---|---|---|
host_system |
7 | Disco, memoria, swap, load y ContainerDown de todos los hosts |
nas_health |
5 | NAS vivo, load, memoria, volumen, temperatura |
nas_storage |
7 | Integridad de almacenamiento: RAID, mdadm, BTRFS, PCIe, cache NVMe |
nas_smart |
15 | Sólo salud de disco reportada por SMART/NVMe |
backup |
2 | Ocupación del datastore PBS |
services |
5 | WP-Pulse, Caddy LXC, Remote-Pulse y contenedores de media |
red_network |
11 | Red: internet/router ASUS/AiMesh/switch+AP UniFi/exporters caídos, router carga/memoria, nuevo cliente visto — grupo nuevo, no documentado antes |
🕰️ Legacy — flujo n8n (complementario / en retirada)¶
Antes de Alertmanager, TODA alerta pasaba por n8n (Beszel Alert Direct Webhook, LXC 200, endpoint POST http://192.168.0.200:5678/webhook/beszel-alert-direct), que clasificaba severity, aplicaba cooldown 5min y reenviaba a Telegram vía la API nativa.
Estado actual: - beszel fue decomisado (LXC 249 retirado), así que su origen de alertas ya no existe. - Alertmanager sustituyó a n8n como sistema central de alertas. - El workflow n8n puede seguir vivo para orígenes puntuales que aún posteen a su webhook (Healthchecks, morning report, watchdog), pero no es el camino canónico. Trátalo como complementario/legacy y no añadas nuevas integraciones ahí.
Detalle del payload/nodos del workflow legacy (por si hay que depurar algo que aún lo use):
- Format 1 (simple):
{source, severity, title, message, url?, skip_cooldown?}. - Format 2 (Grafana Alertmanager): n8n auto-detecta
alerts: [...]. - Nodo
Process Alert v2: auto-detect Grafana vs simple, clasificación de severity por keyword, cooldown 5min por hashsource:severity:title[:40]:message[:60], log enstaticData.alertLog(últimas 200). - Extraer:
pct exec 200 -- n8n export:workflow --id=dKTsixDYpxc76rYI --output=/tmp/wf.json.
🔧 Troubleshooting¶
"Alerta de Prometheus disparó pero no llegó a Telegram"¶
Verificar Alertmanager y su config:
# Estado del container
ssh [email protected] 'docker ps | grep alertmanager'
# Alertas activas / silences
curl -s http://192.168.0.208:9093/api/v2/alerts | jq .
# Config cargada
curl -s http://192.168.0.208:9093/api/v2/status | jq .config
# Logs
ssh [email protected] 'docker logs alertmanager --tail 50'
Comprobar que el token de bot existe: /etc/alertmanager/telegram-bot-token dentro del container.
"No llegan alertas de Proxmox a Telegram"¶
Verificar el endpoint webhook y el matcher en PVE:
ssh [email protected] 'cat /etc/pve/notifications.cfg'
# Probar el endpoint
ssh [email protected] 'pvesh create /cluster/notifications/endpoints/webhook/telegram/test 2>/dev/null || echo "usa la UI: Datacenter -> Notifications -> Test"'
"backup-freshness.sh no avisa / avisa de más"¶
# Ejecución manual (con umbral bajo para test)
ssh [email protected] 'THRESH_H=1 /usr/local/sbin/backup-freshness.sh'
# Log
ssh [email protected] 'tail -20 /var/log/backup-freshness.log'
TG_BOT/TG_CHAT en /root/.openclaw/secrets.env.
"Grafana alert disparó pero no llegó"¶
curl -s -u admin:$GRAFANA_ADMIN_PASS http://192.168.0.208:3030/api/v1/provisioning/contact-points
curl -s -u admin:$GRAFANA_ADMIN_PASS http://192.168.0.208:3030/api/v1/provisioning/policies
📍 Componentes desactivados¶
- OpenClaw watchdog timer (
openclaw-watchdog.timer) — DISABLED. Causaba spam de alertas durante maintenance. Beszel Alert Forwarderworkflow (UIDvj0ZOqNN4vkSkzYG) — DISABLED, versión vieja basada en poll a ntfy topicbeszel-alerts.- Beszel (LXC 249) — decomisado. Ya no emite alertas.