Jede Nacht um 01:00 lief der vzdump-Job auf pve-dagobah. Jede Nacht seit dem 17. August endete er für einen einzigen Container mit job errors. Alle anderen Guests backupten sauber durch. Zabbix sammelte in dieser Zeit stur weiter Daten — nur gemeldet hat er nichts. Zehn Tage lang.
TL;DR:
ct-r2d2(VMID 114) hing seit ~17.08. mitlock: backupfest — ein Cleanup-Cron loggte “Unlock”, entfernte den Lock aber nicht wirklich- Zabbix hatte den Fehler technisch im System, aber vier unabhängige Lücken ließen den Ausfall unbemerkt
- Monitoring, das “sammelt aber nicht alarmiert”, ist schlechter als gar kein Monitoring — es gibt dir das Gefühl, abgesichert zu sein
Das Problem
Backup-Job auf pve-dagobah läuft nachts durch die ganze Guest-Liste. Für ct-r2d2 (VMID 114) kam seit rund zehn Tagen immer dasselbe Ergebnis: job errors. Kein Absturz, kein lauter Fehler irgendwo im Monitoring-Dashboard — einfach ein Container, der in der Backup-Rotation fehlte.
Die anderen Guests auf demselben Host liefen unauffällig durch. Das Problem war auf einen einzigen Container begrenzt, was die Suche erst mal einfacher machte — und gleichzeitig erklärt, warum niemand aufmerksam wurde: ein fehlgeschlagener Job von vielen sieht auf den ersten Blick nach Rauschen aus, nicht nach Symptom.
Die Diagnose
Erster Griff: Storage-Status und ob der Container überhaupt für ein Backup bereit ist.
pvesm status
# Bad: alles sieht normal aus
local-zfs zfspool active ...
pbs-backup pbs active ...
Storage ist grün — das war also nicht die Ursache. Der nächste Schritt: direkt im Config-Verzeichnis nachsehen, ob ein Lock-File für diese VMID liegt.
ls -la /var/lib/vz/images/114/ 2>/dev/null
cat /etc/pve/lxc/114.conf | grep lock
# Bad
lock: backup
Da war er. lock: backup auf CT 114 — ein Zustand, der normalerweise nur existiert, während ein vzdump-Lauf aktiv ist. Der letzte Lauf war aber längst vorbei. Das ist ein klassischer Stale Lock: der Job ist (aus welchem Grund auch immer) nicht sauber durchgelaufen oder abgebrochen, und das Lock-Flag wurde nie zurückgesetzt. Jeder nachfolgende vzdump-Versuch auf diese VMID bricht sofort ab, weil Proxmox korrekterweise kein paralleles Backup auf einen gelockten Guest zulässt.
Interessant wurde es beim Blick auf den vorhandenen Cleanup-Mechanismus: Es gab bereits ein cleanup-stale-backup-locks.sh-Script, das genau solche Locks abräumen sollte — und laut Cron-Log hatte es auch “unlocked” gemeldet. Nur: der Lock war danach immer noch da.
grep -i "114" /var/log/cleanup-stale-backup-locks.log | tail -5
# Bad — Log sagt "erledigt", Realität sagt "nein"
2026-08-27 03:00:12 unlock VMID 114 — OK
2026-08-28 03:00:11 unlock VMID 114 — OK
Das Script loggte Erfolg, ohne danach zu verifizieren, dass der Lock wirklich weg war. Ein klassischer “ich habe den Befehl abgesetzt, also nehme ich Erfolg an”-Bug — kein Fehlerpfad, kein zweiter Blick aufs Ergebnis.
Die Ursache
Backup-Seite: ct-r2d2 (114) hielt seit ~17.08. den Lock backup. Der Cleanup-Cron lief zwar, loggte “Unlock” — aber der Lock blieb bestehen. Zehn Nächte hintereinander derselbe Fehlschlag.
Das Perfide: Das eigentliche Problem war nicht nur der Stale Lock selbst — Proxmox-Hosts bekommen so was ab und zu, dafür gibt es Cleanup-Mechanismen. Das Perfide war, dass Zabbix diesen Zustand zehn Tage lang nicht meldete, obwohl mehrere getrennte Monitoring-Bausteine theoretisch dafür zuständig gewesen wären — und jeder einzelne davon unabhängig kaputt oder unvollständig konfiguriert war.
Vier Schichten, vier unterschiedliche Ausfallarten:
1. Generische vzdump-Trigger waren deaktiviert — seit März.
Die generischen Trigger, die auf vzdump.status=2 (Job-Fehler) reagieren sollten, waren seit dem 23.03. disabled. Nicht kaputt, nicht falsch konfiguriert — bewusst abgeschaltet (sie galten auf diesem Host als irreführend), während die granulareren Per-Guest-Items in derselben Umstellung nur für die damals vorhandenen VMIDs angelegt wurden. Die später dazugekommenen Guests — darunter 114 — bekamen nie ein eigenes Item.
2. Per-VM-Items fehlten für genau die betroffenen VMIDs.
Die jobs.cfg enthielt die VMIDs 109, 110, 111, 112, 113, 114 (plus ein Age-Item für 103) — aber für keine davon existierte ein Per-VM-Zabbix-Item. Das Monitoring-Konzept war von generisch auf granular umgestellt worden. Nur: die neuen Guests, die nach der Umstellung dazukamen, wurden nie nachgetragen. Genau die VMID, die gerade das Problem hatte, war eine von denen, für die nie ein Item angelegt wurde.
3. stale_locks stand permanent auf 0 — wegen fehlendem sudo.
Es gab bereits einen Zabbix-Key pve.cluster.stale_locks, der genau diesen Fall erkennen sollte: Locks, die länger bestehen als ein normaler Backup-Lauf dauert. Das Problem: Der zabbix-User hatte kein Leserecht auf die relevante Proxmox-Config (0640 root:www-data). Jeder Poll lief ins Leere und lieferte 0 — nicht weil keine Stale Locks existierten, sondern weil der Check sie gar nicht sehen konnte.
sudo -u zabbix cat /etc/pve/lxc/114.conf
# Bad
cat: /etc/pve/lxc/114.conf: Permission denied
# Good — Berechtigung bestätigt das Problem
ls -la /etc/pve/lxc/114.conf
-rw-r----- 1 root www-data 412 Aug 28 09:14 /etc/pve/lxc/114.conf
zabbix ist weder root noch Mitglied in www-data. Der Check war von Tag eins an blind — stale_locks zeigte immer 0, unabhängig vom tatsächlichen Zustand.
4. Fehlende Eskalation. Es gab einen Job-Level-Trigger auf dem Zabbix-Host-Objekt 10584, der am 17./18.08. tatsächlich einmal gefeuert hatte — eine Telegram-Nachricht ging raus (Severity st=1). Aber ohne Eskalationslogik dahinter verschwand die Meldung im Kanal, ohne dass jemand nach Tag zwei noch mal erinnert wurde. Ein einzelner Alert ohne Wiederholung ist in der Praxis: ein Alert, den man genau einmal sieht und danach vergisst.
Vier unabhängige Ausfälle, die sich gegenseitig deckten: Der generische Trigger war seit Monaten aus, der Per-VM-Trigger existierte für genau diese VMID nie, der Stale-Lock-Check war blind — und der eine Alert, der tatsächlich feuerte, eskalierte nicht. Monitoring, das technisch “läuft”, aber an jeder einzelnen Entscheidungsstelle versagt, ist schwerer zu finden als ein komplett totes Monitoring — weil es auf den ersten Blick so aussieht, als wäre alles in Ordnung.
Die Lösung
1. Lock entfernen und Cleanup-Script hardenen
Der Stale Lock auf CT 114 wurde entfernt:
# LXC → pct; für QEMU-VMs entsprechend qm unlock <vmid>
pct unlock 114
# Good
cat /etc/pve/lxc/114.conf | grep lock
# (keine Ausgabe — Lock weg)
Das cleanup-stale-backup-locks.sh-Script wurde um eine echte Verifikation nach dem Unlock-Versuch erweitert — Check ob der Lock wirklich weg ist, ein sed-Fallback falls der reguläre Unlock-Pfad scheitert, und ein Error-Log statt stillschweigendem “OK” bei Fehlschlag.
2. Zabbix: sudo für den stale_locks-Check
Der zabbix-User braucht Leserechte auf die relevanten PVE-Configs, ohne pauschal root-Rechte zu bekommen. Lösung: gezieltes sudoers-Rule für genau das Check-Script, nicht für beliebige Befehle.
# /etc/sudoers.d/zabbix-pve-stale-locks
zabbix ALL=(root) NOPASSWD: /usr/local/bin/check_pve_stale_locks.sh
Das Script selbst muss root:root gehören und 0755 sein — und darf für den zabbix-User nicht beschreibbar sein. Ein NOPASSWD-Eintrag auf ein Script, das ein Nicht-root-User ändern darf, ist eine Root-Lücke.
Das UserParameter ruft das Script über sudo auf statt es direkt als zabbix-User zu lesen:
UserParameter=pve.cluster.stale_locks,sudo /usr/local/bin/check_pve_stale_locks.sh
zabbix_get -s 127.0.0.1 -k pve.cluster.stale_locks
# Good — jetzt sieht der Check tatsächlich existierende Stale Locks
0
(Der Wert ist nach dem Fix korrekt 0, weil der Lock bereits entfernt war — der entscheidende Unterschied zu vorher: der Check kann jetzt überhaupt etwas anderes als 0 liefern.)
3. Fehlende Per-VM-Items und -Trigger nachziehen
Für alle VMIDs aus jobs.cfg, die noch keine eigenen Items hatten (109, 110, 111, 112, 113, 114 plus Age-Item für 103), wurden 13 neue Items und 21 neue Trigger angelegt — pro VMID ein Status-Item und ein Age-Item, mit jeweils eigenem Trigger statt eines generischen Sammel-Triggers.
4. Generische Trigger reaktivieren
Die seit März deaktivierten generischen Trigger (IDs 25565–25567) wurden wieder enabled — als zusätzliches Sicherheitsnetz, nicht als Ersatz für die Per-VM-Trigger. Zwei Monitoring-Schichten, die sich gegenseitig abdecken, statt einer einzigen Schicht mit stillen Lücken.
5. Eskalation statt Einmal-Alert
Die Job-Level-Trigger-Action (Action #3) bekam eine echte Eskalationsstufe: Step 2 wiederholt den Alert alle 24 Stunden, solange das Problem offen ist. Zusätzlich manual_close auf den Job-Triggern aktiviert, damit ein Ops-Mensch den Fall explizit bestätigen muss, statt dass er automatisch recovered, sobald der nächste Lauf zufällig durchgeht.
Nach dem Fix: vier neue PROBLEM-Events wurden angelegt (closed-loop statt stillschweigend überschrieben), Telegram + Smart-Notify mit Severity st=1 — sichtbar, nachvollziehbar, mit Historie.
Noch offen zum Zeitpunkt des Fixes: Der nächste planmäßige vzdump-Lauf um 01:00 musste erst zeigen, dass CT 114 sauber durchläuft — danach erst konnten die offenen Backup-PROBLEMs als recovered geschlossen werden. Ein manueller Nachhol-Lauf tagsüber war bewusst keine Option (Backup-Pfad-Hairpin — tagsüber instabil).
Lessons Learned
“Zabbix sammelt Daten” ist nicht dasselbe wie “Zabbix alarmiert”. Ein Item, das korrekt gepollt wird, aber keinen aktiven Trigger hat — oder dessen Trigger disabled ist — ist für die Praxis nutzlos. Der Wert steht im Dashboard, aber niemand wird geweckt.
Ein Cleanup-Script, das Erfolg loggt ohne zu verifizieren, ist schlimmer als gar kein Script. Es erzeugt genau das falsche Vertrauen: “Das wird doch automatisch aufgeräumt” — während der Lock still bestehen bleibt.
Eine Monitoring-Migration (generisch → Per-VM) muss für jeden neuen Guest nachgezogen werden. Sonst entsteht eine Lücke genau für die Systeme, die nach dem Umbau dazukommen — und das bemerkt man erst, wenn genau dort etwas kaputtgeht.
Berechtigungsprobleme bei Monitoring-Checks liefern selten einen Fehler — sie liefern einen falschen, plausibel aussehenden Wert.
stale_locks=0sieht nicht wie ein Fehler aus. Es sieht aus wie “alles gut”. Nur ein Blick auf die Dateiberechtigungen des Checks selbst deckt das auf.Ein Alert ohne Eskalation ist ein Alert, der genau einmal gesehen wird. Wenn ein Problem über Tage besteht, muss die Benachrichtigung das widerspiegeln — sonst verschwindet die einzige Warnung im Kanal-Rauschen nach Tag eins.
Mehrere unabhängige Monitoring-Lücken, die sich gegenseitig decken, sind schwerer zu finden als ein einziger kompletter Ausfall. Jede einzelne Schicht sah für sich genommen “halbwegs funktionierend” aus. Erst die Kombination aller vier Lücken erzeugte die vollständige Stille.
Checkliste
-
pvesm statusprüfen — Storage grün, bevor du tiefer gräbst -
/etc/pve/lxc/<VMID>.confbzw./etc/pve/qemu-server/<VMID>.confauflock:-Zeile prüfen - Cleanup-Scripts: Verifizieren sie nach dem Unlock-Versuch tatsächlich den neuen Zustand, oder loggen sie nur “OK”?
- Generische UND Per-VM-Trigger im Monitoring: beide aktiv, keine stille Deaktivierung seit einer früheren Migration?
- Jede VMID aus
jobs.cfghat ein eigenes Monitoring-Item — Abgleichjobs.cfggegen Zabbix-Item-Liste - Permission-Check für Monitoring-Scripts: Kann der Monitoring-User die Dateien, die er prüfen soll, überhaupt lesen? (
sudo -u <monitoring-user> cat <datei>) - Trigger-Eskalation: Wiederholt sich der Alert, solange das Problem offen bleibt, oder verhallt er nach einer Nachricht?
- Nach dem Fix: nächsten planmäßigen Lauf abwarten und als Closed-Loop-Bestätigung verwenden, nicht nur den manuellen Fix als “erledigt” markieren