Ein Windows-Server startet neu. Geplant, kontrolliert, kein Drama.
Dein Handy sieht das anders. 65 Benachrichtigungen. Jeder einzelne Windows-Dienst der beim Hochfahren kurz nicht lief, bekommt seinen eigenen Alert. Zwei Minuten später: 65 Recovery-Meldungen. Du scrollst durch 130 Nachrichten um festzustellen — alles in Ordnung.
Das ist kein Monitoring. Das ist Spam.
Das Problem
Zabbix entdeckt Windows-Dienste per Low-Level Discovery (LLD). Für jeden Dienst mit Startup-Typ “automatic” wird ein Trigger erstellt:
"PrintSpooler" (Druckwarteschlange) is not running (startup type automatic)
Bei einem Reboot passiert folgendes:
- Server fährt runter → Agent wird unerreichbar
- Server bootet → Agent startet → Dienste starten nacheinander
- Zabbix pollt und sieht: 65 Dienste nicht gestartet → 65 Alerts
- 30 Sekunden später: Dienste laufen → 65 Recovery-Meldungen
Zwischen Schritt 2 und 4 liegt die Flut.
Die Lösung: Trigger Dependencies
Zabbix hat ein Feature genau dafür: Trigger Dependencies. Die Logik:
Wenn Trigger A (Parent) im PROBLEM-Status ist, werden alle abhängigen Trigger B, C, D unterdrückt.
Der Plan:
- Parent-Trigger: “Host has been restarted (uptime < 30m)”
- Abhängige Trigger: Alle “Service is not running” Trigger
Bei einem Reboot feuert nur der Uptime-Trigger. Alle Service-Alerts bleiben still. Nach 30 Minuten löst sich der Parent — und wenn dann ein Dienst immer noch nicht läuft, feuert dessen Trigger ganz normal.
Eine Meldung statt 65.
Die Umsetzung
Schritt 1: Den richtigen Hebel finden
Die Service-Trigger werden per LLD erstellt — du kannst sie nicht einzeln anfassen. Aber du kannst den Trigger-Prototypen auf dem Template ändern. Das ist der Bauplan, aus dem LLD die konkreten Trigger generiert.
Zuerst die Discovery Rule auf dem Template finden:
# Template: "Windows by Zabbix agent"
curl -sk -X POST "https://<DEIN-ZABBIX>/api_jsonrpc.php" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "discoveryrule.get",
"params": {
"hostids": ["<TEMPLATE-ID>"],
"search": {"name": "service"},
"output": ["itemid", "name"]
},
"auth": "<TOKEN>",
"id": 1
}'
Dann den Trigger-Prototypen:
curl -sk -X POST "https://<DEIN-ZABBIX>/api_jsonrpc.php" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "triggerprototype.get",
"params": {
"discoveryids": ["<DISCOVERY-RULE-ID>"],
"output": ["triggerid", "description"],
"selectDependencies": ["triggerid"]
},
"auth": "<TOKEN>",
"id": 1
}'
Du brauchst zwei IDs:
- Die Trigger-Prototype-ID des Service-Triggers
- Die Trigger-ID des Uptime-Triggers auf demselben Template
Schritt 2: Dependency auf dem Prototypen setzen
curl -sk -X POST "https://<DEIN-ZABBIX>/api_jsonrpc.php" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "triggerprototype.update",
"params": {
"triggerid": "<SERVICE-PROTOTYPE-ID>",
"dependencies": [
{"triggerid": "<UPTIME-TRIGGER-ID>"}
]
},
"auth": "<TOKEN>",
"id": 1
}'
Wenn du sowohl passive als auch active Agent-Templates nutzt, musst du das auf beiden Templates machen.
Schritt 3: Der Pitfall — bestehende Trigger
Jetzt der Teil den die Doku verschweigt.
Die Prototype-Dependency gilt nur für neue Trigger die LLD in Zukunft erstellt. Die hunderte bestehenden Service-Trigger auf deinen Hosts? Unverändert. Kein Dependency. Kein Schutz.
Dein erster Instinkt: trigger.update auf die bestehenden Trigger.
"Cannot update dependencies for a discovered trigger"
Zabbix erlaubt kein trigger.update mit dependencies auf discovered Triggers. Punkt. Kein Workaround.
Die Lösung: LLD muss die Trigger neu verarbeiten. Beim nächsten Discovery-Lauf liest LLD den aktualisierten Prototypen und propagiert die Dependencies auf alle bestehenden Trigger.
Das Problem: Die Discovery Rule läuft nur stündlich (Default). Wenn du 50 Hosts hast, wartest du bis zu einer Stunde.
Schritt 4: LLD-Rerun forcieren
Per API kannst du einen sofortigen Discovery-Lauf erzwingen:
# Discovery Rule ID des Hosts ermitteln
curl -sk -X POST "https://<DEIN-ZABBIX>/api_jsonrpc.php" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "discoveryrule.get",
"params": {
"hostids": ["<HOST-ID>"],
"search": {"key_": "service.discovery"},
"output": ["itemid"]
},
"auth": "<TOKEN>",
"id": 1
}'
# Sofortigen Check auslösen
curl -sk -X POST "https://<DEIN-ZABBIX>/api_jsonrpc.php" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "task.create",
"params": {
"type": 6,
"request": {
"itemid": "<DISCOVERY-RULE-ID>"
}
},
"auth": "<TOKEN>",
"id": 1
}'
Das machst du einmal pro Host. Bei 50 Hosts ein kurzes Bash-Script, nach 20 Sekunden sind die Dependencies überall angekommen.
Achtung: Hosts die aktuell nicht überwacht werden (disabled, nicht erreichbar) bekommen die Dependencies automatisch beim nächsten erfolgreichen LLD-Lauf — sobald sie wieder online sind.
Schritt 5: Uptime-Threshold anpassen
Der Standard-Trigger “Host has been restarted” prüft uptime < 10m. Das kann zu knapp sein:
- Server mit vielen Diensten brauchen länger zum Hochfahren
- Hohe CPU-Last beim Booten verzögert den Zabbix-Agent-Start
- Wenn der Agent erst nach 10 Minuten antwortet, verpasst Zabbix den niedrigen Uptime-Wert komplett
Sicherer: 30 Minuten.
curl -sk -X POST "https://<DEIN-ZABBIX>/api_jsonrpc.php" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "trigger.update",
"params": {
"triggerid": "<TEMPLATE-UPTIME-TRIGGER-ID>",
"expression": "last(/<TEMPLATE-NAME>/system.uptime)<30m"
},
"auth": "<TOKEN>",
"id": 1
}'
Template-Änderungen propagieren automatisch auf alle Hosts — hier kein LLD-Rerun nötig.
Das Ergebnis
Vorher:
14:23:01 PROBLEM SRV-R2D2: Host has been restarted (uptime < 10m)
14:23:03 PROBLEM SRV-R2D2: "Spooler" is not running
14:23:03 PROBLEM SRV-R2D2: "W32Time" is not running
14:23:04 PROBLEM SRV-R2D2: "WinRM" is not running
... (62 weitere)
14:25:31 OK SRV-R2D2: "Spooler" recovered
14:25:31 OK SRV-R2D2: "W32Time" recovered
... (62 weitere)
130 Nachrichten. Null Informationsgehalt.
Nachher:
14:23:01 PROBLEM SRV-R2D2: Host has been restarted (uptime < 30m)
14:52:32 OK SRV-R2D2: Host has been restarted - recovered
Zwei Nachrichten. Volle Information.
Lessons Learned
Trigger Dependencies gehören in den Prototypen — nicht auf die einzelnen Trigger. Die Zabbix API verhindert letzteres bei discovered Triggers aktiv.
Prototype-Änderungen brauchen einen LLD-Rerun um auf bestehende Trigger zu wirken.
task.createmit Type 6 forciert das sofort.10 Minuten Uptime-Threshold ist zu knapp für moderne Windows-Server. 30 Minuten gibt Puffer für langsame Boots, hohe CPU-Last, und verzögerten Agent-Start.
Die beste Alert-Regel ist die, die nicht feuert wenn sie nicht soll. Alert Fatigue ist real — wer 65 Alerts pro Reboot bekommt, ignoriert irgendwann auch den einen der zählt.
Checkliste
- Trigger-Prototypen identifiziert (Template → Discovery Rule → Prototype)
- Dependency gesetzt: Service-Prototype → Uptime-Trigger (pro Template)
- LLD-Rerun auf allen aktiven Hosts forciert (
task.createtype 6) - Uptime-Threshold geprüft und ggf. angepasst (Empfehlung: 30m)
- Trigger-Description aktualisiert (falls Threshold geändert)
- Testlauf: Server neustarten und prüfen ob nur Uptime-Alert kommt