Du baust eine Self-Healing Monitoring Pipeline: Zabbix erkennt Probleme, n8n routet und versucht automatisch zu fixen, und bei Fehlschlag eskaliert eine KI die Diagnose per Telegram. Klingt nach der Zukunft.
Realität: 852 Alerts empfangen, 0 mal repariert. Kein Telegram. Keine Diagnose. Nur stille Datenbankeinträge.
Hier ist die Geschichte, wie ich das debuggt habe — und warum die beste Lösung am Ende kein vollautonomer Bot war, sondern ein KI-Assistent der vorschlägt und auf dein OK wartet.
Die Architektur (auf dem Papier)
Zabbix Trigger
→ Wrapper-Script (bash)
→ n8n Alert Router (Webhook)
→ Parse, Route, Log
→ Remediation Engine (SSH-Fix)
→ Bei Fehlschlag: KI-Diagnose
→ Telegram: "Fix vorschlagen"
Drei Layer. Jeder einzelne war kaputt.
Bug 1: Das Henne-Ei-Problem
Das Wrapper-Script smart-notify.sh im Zabbix-Container schickte Alerts an:
WEBHOOK_URL="https://flow.darkside.local/webhook/zabbix-alert"
Diese URL geht über den Reverse Proxy (nginx auf einem separaten Host). Wenn nginx down ist, feuert Zabbix einen Alert — und genau dieser Alert kann n8n nicht erreichen, weil der Weg über nginx läuft.
Das Script hatte einen Telegram-Fallback:
if [ "$http_code" != "200" ]; then
# Direkt per Telegram API senden
telegram_text="⚠️ [n8n bypass] ${SUBJECT} ${BODY}"
curl ... "https://api.telegram.org/bot${TOKEN}/sendMessage"
fi
Symptom: Jeder zweite Alert kam als [n8n bypass] in Telegram an. n8n hat ihn nie gesehen.
Fix: Internes Routing als Primary, externes als Fallback:
WEBHOOK_URL_INTERNAL="http://192.168.66.10:8098/webhook/zabbix-alert"
WEBHOOK_URL_EXTERNAL="https://flow.darkside.local/webhook/zabbix-alert"
# Versuch 1: intern (kein Reverse Proxy nötig)
http_code=$(curl ... "$WEBHOOK_URL_INTERNAL")
[ "$http_code" = "200" ] && exit 0
# Versuch 2: extern
http_code=$(curl ... "$WEBHOOK_URL_EXTERNAL")
[ "$http_code" = "200" ] && exit 0
# Versuch 3: Telegram direkt
Lesson: Monitoring-Webhooks sollten nie über die Infrastruktur routen, die sie überwachen.
Bug 2: Der Parser verstand kein Zabbix
Der n8n Alert Router hatte einen Code Node der Zabbix-Nachrichten parste:
trigger_name = extractLine(body, 'Trigger:');
event_id = extractLine(body, 'Event ID:');
if (subject.startsWith('PROBLEM:')) status = 'PROBLEM';
Aber Zabbix sendet:
Problem name: Todesstern-Kantine nicht erreichbar
Host: srv-r2d2
Severity: High
Original problem ID: 5703535
Drei Mismatches:
Trigger:→ Zabbix sagtProblem name:Event ID:→ Zabbix sagtOriginal problem ID:PROBLEM:(Großbuchstaben) → Zabbix sagtProblem:(mixed case)
Ergebnis: Jeder Alert wurde mit trigger_name: "", event_id: <timestamp>, status: UNKNOWN geloggt. Die Severity-basierte Routing-Logik funktionierte nie.
Fix:
trigger_name = extractLine(body, 'Problem name:', 'Trigger:');
event_id = extractLine(body, 'Original problem ID:', 'Event ID:');
const subjectLower = subject.toLowerCase();
if (subjectLower.startsWith('problem:') || subjectLower.startsWith('problem ')) {
status = 'PROBLEM';
} else if (subjectLower.startsWith('resolved') || body.includes('has been resolved')) {
status = 'RESOLVED';
}
extractLine akzeptiert jetzt mehrere Prefixes als Fallback-Kette.
Bug 3: Switch v3 Nodes crashen in n8n 2.x
Der Alert Router hatte zwei Switch-Nodes (v3) für Routing nach Suppress-Pattern und Severity. Beide crashten mit:
TypeError: Cannot read properties of undefined (reading 'push')
Switch v3 Nodes sind in n8n 2.13.3 inkompatibel mit der API-Validierung. Weder Import noch Aktivierung funktionierte. Switch v1 hatte den gleichen Fehler.
Fix: Beide Switch-Nodes durch einen einzigen If-Node ersetzt:
Severity >= 3 (Average)?
→ Ja: Telegram Escalation + KI-Diagnose
→ Nein: Telegram Info + DB-Log
Simpler, robuster, keine Node-Version-Probleme.
Bug 4: $json-Kontext geht nach Postgres verloren
Die Telegram-Node verwendete {{ $json.chat_id }} für den Empfänger. Aber nach dem Postgres INSERT sah $json so aus:
{"id": 873, "event_id": "5703535", "host": "srv-r2d2", "severity": 4, "status": "PROBLEM", "action": "pending"}
Kein chat_id — das steht nicht in der alert_log Tabelle. Kein severity_str — nur der numerische Wert.
Symptom: 400 - chat_id is empty
Fix: Chat-ID hardcoden (es geht sowieso immer an den gleichen Channel), Severity-Text aus dem numerischen Wert ableiten:
severity == 5 ? 'Disaster' : severity == 4 ? 'High' : severity == 3 ? 'Average' : 'Information'
Bug 5: If-Node hat vertauschte Outputs
n8n If v2 Node: Output 0 ist TRUE, Output 1 ist FALSE. Oder?
Nein. In der Praxis (n8n 2.13.3) war es umgekehrt. Ein PROBLEM-Alert mit status !== "RESOLVED" landete im TRUE-Branch (Mark Resolved) statt im FALSE-Branch (Route weiter).
Fix: Connections getauscht. Output 0 → Suppress Pattern, Output 1 → Mark Resolved.
Keine Doku dazu gefunden. Trial and Error.
Das eigentliche Problem: Vollautomatisch ist falsch
Nachdem alle Bugs gefixt waren, lief die Pipeline. n8n empfing Alerts, loggte sie, schickte Telegram. Aber die automatische Remediation (SSH auf den Host, Service neustarten) war kaputt — SSH-Keys nicht deployed, Playbooks nicht gematchet.
Und dann die Frage: Will ich das überhaupt vollautomatisch?
Nein. Ein Bot der eigenständig Services neustartet, Configs ändert oder Disk-Space aufräumt ist ein Risiko. Besonders wenn die KI-Diagnose falsch liegt.
Die bessere Architektur: Human-in-the-Loop
Zabbix Alert
→ n8n: Parse, Route, Log
→ Telegram: "🟠 HIGH - Todesstern-Kantine down"
→ Claude AI: Diagnostiziert per SSH
→ Telegram: "Root Cause: Container gestoppt (Exit 0)"
"Vorgeschlagener Fix: docker compose up -d"
"Risiko: Niedrig"
[✅ Fix ausführen] [❌ Ablehnen]
Die KI macht die Arbeit — SSH-Zugang, Log-Analyse, Root-Cause-Diagnose. Aber der Mensch entscheidet. Ein Knopfdruck auf dem Handy.
Implementation
Der Webhook-Endpunkt auf dem Claude-Server empfängt den Alert von n8n:
@app.router.add_post("/webhook/zabbix")
async def handle_zabbix_webhook(request):
host = data.get("host")
trigger = data.get("trigger_name")
severity = data.get("severity")
# Claude-Session starten
asyncio.create_task(handler._start_diagnosis(alert, autonomy))
Claude diagnostiziert mit einem Prompt der explizit sagt:
WICHTIG: Führe KEINE Fixes durch! Nur diagnostizieren.
Liefere: 1) Root-Cause-Analyse 2) Konkreter Fix-Vorschlag 3) Risiko-Einschätzung
Nach der Diagnose: Approval-Buttons in Telegram:
buttons = [
InlineKeyboardButton("✅ Fix ausführen", callback_data=f"approve:{conv_name}"),
InlineKeyboardButton("❌ Ablehnen", callback_data=f"reject:{conv_name}"),
]
# Fix-Zusammenfassung aus Claude's Response extrahieren
await bot.send_message(chat_id,
f"Vorgeschlagener Fix:\n{fix_summary}\n\nSoll ich das durchführen?",
reply_markup=InlineKeyboardMarkup([buttons]))
Bei “Fix ausführen” wird die Claude-Session fortgesetzt mit "Freigabe erteilt. Fahre fort.". Bei “Ablehnen” wird die Session beendet.
Bonus: Kreditkarten-Workflows waren auch kaputt
Nebenbei entdeckt: Drei IMAP-basierte Workflows für Kreditkartenabrechnung waren seit der n8n 2.x Migration komplett kaputt:
| Problem | Fix |
|---|---|
| IMAP-Trigger feuert nicht in n8n 2.x | Schedule Trigger (Cron alle 15 Min) hinzugefügt |
| AI Agent crasht bei leerer Mailbox (96x/Tag!) | If-Guard: keine Mail → Skip |
| Self-Signed Cert blockiert IMAP | NODE_TLS_REJECT_UNAUTHORIZED=0 |
Lessons Learned
Monitoring nie über die überwachte Infra routen. Webhook direkt an den Service, nicht über den Reverse Proxy.
n8n Node-Versionen sind ein Minenfeld. Switch v3, Webhook v2, httpRequest v4.2 — jede Version hat andere Parameter-Formate. Im Zweifel v1 nehmen und testen.
$json-Kontext tracken. Nach jedem Postgres/HTTP-Node ändert sich$json. Werte die du später brauchst müssen im Output enthalten sein.Vollautomatische Remediation ist ein Anti-Pattern. KI-Diagnose + menschliche Freigabe ist der Sweet Spot. Die KI macht die Arbeit, der Mensch trägt die Verantwortung.
“success” heißt nicht “funktioniert”. Ein n8n Workflow der
successzeigt kann trotzdem nichts tun — wenn der If-Guard alles in den Skip-Branch schickt, ist das technisch erfolgreich.
Checkliste: Self-Healing Pipeline bauen
- Webhook-URL intern (kein Reverse Proxy in der Kette)
- Alert-Parser gegen echte Zabbix-Nachrichten getestet (nicht gegen die Doku)
- Node-Versionen kompatibel mit deiner n8n-Version
-
$json-Kontext nach jedem Node verifiziert - Telegram-Credentials mit echten IDs (nicht Placeholder)
- KI-Diagnose: nur vorschlagen, nicht ausführen
- Approval-Flow: konkreter Fix im Button-Text sichtbar
- Fallback: wenn alles fehlschlägt, trotzdem Telegram-Notification