Schauma Mal, dann wird’s schon (automatisiert) werden
Ich habe ja schon in einem früheren Beitrag beschrieben, wie man mit Home Assistant einen Rechner automatisch starten lassen kann. Hier geht es jetzt darum den Backupserver nach getaner Arbeit wieder automatisch auszuschalten, passenderweise natürlich am Tag der Arbeit! Ich habe das die letzte Zeit manuell gemacht, was auf Dauer weder praktisch noch empfehlenswert ist. Zumindest hab’ ich so ein Gefühl bekommen, wie lange die jeweiligen Update-Schritte benötigen. Insbesondere das Validieren der Backups im Proxmox Backup Server dauert länger als gedacht, nämlich um die vierzig Minuten. Das ist länger als das Sichern von drei Proxmox-Instanzen dauert! Von dem her war es wahrscheinlich nicht schlecht, dass ich den Prozess am Anfang noch persönlich überblickt habe und nicht sofort das Herunterfahren automatisiert habe.
Aber jetzt habe ich lange genug auf die running jobs beim PBS geschaut und es ist Zeit für Automatisierung!
Der Plan ist wie folgt: Zuerst wird in der PBS VM überprüft, ob nach zwölf noch ein Job läuft, wenn nicht, schaltet sich die VM ab. Auf Host-Ebene wiederum überprüft PVE zur selben Zeit, ob die PBS VM noch läuft. Wenn nicht, dann schaltet sich der ganze Backupserver ab. Dieser Ansatz ist jedoch nur der erste Schritt, denn zukünftig werden auch noch andere Backups direkt auf die zweite VM des Systems übertragen werden, nämlich auf TrueNAS Scale. Da dies zurzeit noch nicht der Fall ist, werde ich erst einmal diese einfache Variante umsetzen und später dann erweitern.
Mit Systemd Service Unit PBS VM herunterfahren lassen
Da ich in Punkto Skript-Schreiberei sehr unkreativ bin, befrage ich dazu gerne einen Chatbot und lasse mir so ein kleines Progrämmchen schreiben.
So möchte das Modell o4-mini das Problem angehen…
… und gibt diesen CLI-Befehl an:
cat << 'EOF' | sudo tee /usr/local/bin/pbs-autoshutdown.sh
#!/bin/bash
# Only proceed after 00:05
if [ "$(date +%H%M)" -lt "0005" ]; then exit 0; fi
# Poll until no PBS tasks are running
while proxmox-backup-manager task list | grep -q running; do
sleep 30
done
# Shut down VM
shutdown -P now
EOF
sudo chmod +x /usr/local/bin/pbs-autoshutdown.sh
Das Skript darin sieht so aus:
#!/bin/bash
# Only proceed after 00:05
if [ "$(date +%H%M)" -lt "0005" ]; then exit 0; fi
# Poll until no PBS tasks are running
while proxmox-backup-manager task list | grep -q running; do
sleep 30
done
# Shut down VM
shutdown -P now
Achtung: Bei OpenAIs neuen Modellen o4-mini und o3 werden inzwischen gerne eine Art Wasserzeichen eingebaut. Das hat wohl das Ziel LLM-kreierte Texte zu identifizieren – was ja an sich gar nicht so schlecht ist – aber in diesem Fall dazu führt, dass der Code nicht mehr geparst und daher auch nicht mehr ausgeführt werden kann. Das ist zum Beispiel hier vor dem “while” der Fall, wie VSCodium zeigt:
Ich habe das “Wasserzeichen” hier entfernt, aber man soll immer vorsichtig sein, bei automatisch generierten Text und Code, inzwischen sogar auf der Zeichenebene. Aber zurück zum Skript selbst.
Das Skript hat also eine Bedingung, dass es erst nach fünf nach zwölf aktiv wird und dann alle 30 Sekunden proxmox-backup-manager task list
ausführt und nach dem Begriff “running” sucht. Erst wenn der Begriff nicht mehr vorkommt, dann wird die while Schleife beendet und die VM heruntergefahren (mit dem Befehl shutdown -P now
, das -P
steht für poweroff). Sieht so weit so gut aus.
Mich interessiert jedoch zuerst, was der PBS-Befehl eigentlich auswirft und, wenn ein Job läuft, ob da auch wirklich irgendwo running
steht, sonst ist das Skript nicht wirklich funktional.
Ohne laufenden Task…
… wie erwartet kein Ergebnis. Mit Task hingegen…
… wird tatsächlich ein Task angezeigt, mitsamt Status running
. Dieses running
sollte die Schleife also am Schleifendrehen halten und somit PBS am Laufen. Mir scheint das Skript kann so funktionieren.
Einen Fehler hat der obige Befehl dennoch, bei Proxmox (wie auch bei Debian grundsätzlich) gibt es von Haus aus keinen sudo
User, daher gehe ich manuell vor und erstelle das Skript mit nano /usr/local/bin/pbs-autoshutdown.sh
und der Inhalt wird manuell reinkopiert. Anschließend das Skript ausführbar machen mit chmod +x /usr/local/bin/pbs-autoshutdown.sh
.
Die finale Version des Skriptes sieht so aus:
#!/usr/bin/env bash
set -euo pipefail
# Abort unless it’s at least 00:05
if [[ $(date +%H%M) -lt 0005 ]]; then
exit 0
fi
# Wait until no backup tasks are running
while proxmox-backup-manager task list | grep -q running; do
sleep 30
done
shutdown -P now
Jetzt sind wir beim Systemd-Service angekommen und hier schlägt o4-mini folgendes vor:
# /etc/systemd/system/pbs-autoshutdown.service
[Unit]
Description=Auto-shutdown PBS VM when backups complete
[Service]
Type=oneshot
ExecStart=/usr/local/bin/pbs-autoshutdown.sh
Dieser Service wird als einmaliger Befehl ausgeführt (Type=oneshot
) und als Timer…
# /etc/systemd/system/pbs-autoshutdown.timer
[Unit]
Description=Run PBS auto-shutdown script at 00:05 daily
[Timer]
OnCalendar=*-*-* 00:05:00
Persistent=false
[Install]
WantedBy=timers.target
… wird die besprochene Zeit angegeben. Die beiden Optionen “Persistent”1 und “WantedBy” bedeuten folgendes:
Ich habe wiederum nano
verwendet, um den Service und Timer zu erstellen.
Um den Systemd-Service zu starten muss man die beiden Zeilen ausführen:
systemctl daemon-reload
systemctl enable --now pbs-autoshutdown.timer
Das waren die verwendeten Befehle:
Wie man sieht, habe ich wieder den ver-sudo
-ten Befehl vom Chatbot eingefügt, also immer aufmerksam vorgehen und nicht wie ich den Befehl einfach reinkopieren.
Überprüfen kann man den Status des Services mit diesen Befehlen:
systemctl status pbs-autoshutdown.service
systemctl status pbs-autoshutdown.timer
Und es sieht so aus:
Da es noch nicht fünf nach zwölf ist, hat der Timer noch nicht ausgelöst (Trigger: Fri 2025-05-02 00:05:00 CEST; 4h 32min left
) und der Service ist daher nur geladen (loaded (/etc/systemd/system/pbs-autoshutdown.service; static)
) und noch nicht aktiv (inactive (dead)
).
Proxmox Host mit Systemd-Service-Unit runterfahren
Jetzt können wir das Spiel auf der Ebene vom Proxmox Host wiederholen. Diesmal kommt als Vorschlag folgendes:
cat << 'EOF' | sudo tee /usr/local/bin/check-pbs-and-shutdown.sh
#!/bin/bash
VMID=105 # replace with your PBS VMID
# If PBS VM is stopped, shut down host
if qm status $VMID | grep -q stopped; then
shutdown -h now
fi
EOF
sudo chmod +x /usr/local/bin/check-pbs-and-shutdown.sh
Das Skript selbst:
#!/bin/bash
VMID=100 # replace with your PBS VMID
# If PBS VM is stopped, shut down host
if qm status $VMID | grep -q stopped; then
shutdown -h now
fi
Hier wird zuerst eine Variable definiert (VMID=100
) und dann eine Wenn-Dann-Abfrage gemacht, wenn Status stopped
dann ausschalten. Moment, das passiert ja nur einmal. Oder was steht im Timer?
# /etc/systemd/system/pve-check-pbs.timer
[Unit]
Description=Run PBS VM status check at 00:05 daily
[Timer]
OnCalendar=*-*-* 00:05:00
Persistent=true
[Install]
WantedBy=timers.target
Tja, das Werkl läuft wirklich genau einmal am Tag um 00:05, “Prompt mit x, das war wohl nix”.
Was soll denn das? Das habe ich auch o4-mini
gefragt:
Interessant, wie das Sprachmodell sehr menschlich die Verantwortung (die es sowieso nicht haben kann) abwälzt mit der passiven Formulierung “it is easy to assume”, ja ja, so ist sie halt, die schöne neue Welt des vibe programming.
Der neue Vorschlag lautet nun…
#!/bin/bash
# only proceed after 00:05
if [[ $(date +%H%M) -lt 0005 ]]; then
exit 0
fi
VMID=100
if qm status $VMID | grep -q stopped; then
shutdown -h now
fi
… bzw. die finale Variante:
#!/usr/bin/env bash
set -euo pipefail
VMID=100 # change to your PBS VMID
# Only act after 00:05
if [[ $(date +%H%M) -lt 0005 ]]; then
exit 0
fi
# If the VM has stopped, power off the host
if qm status "$VMID" | grep -q stopped; then
shutdown -h now
fi
Jetzt wird nur noch nach fünf nach zwölf in der Nacht ausgeführt. Dann noch ausführbar machen mit chmod +x /usr/local/bin/check-pbs-and-shutdown.sh
.
Wobei das Wichtigste in den Systemd-Units drin steckt.
Service:
# /etc/systemd/system/pve-check-pbs.service
[Unit]
Description=Check PBS VM and shutdown host if conditions met
[Service]
Type=oneshot
ExecStart=/usr/local/bin/check-pbs-and-shutdown.sh
Timer:
# /etc/systemd/system/pve-check-pbs.timer
[Unit]
Description=Periodic check for PBS VM shutdown
[Timer]
OnCalendar=*-*-* 00:05:00 # first shot at 00:05
OnUnitActiveSec=1min # then every minute
Persistent=false
[Install]
WantedBy=timers.target
Jetzt sollte ab fünf nach zwölf jeder Minute das obige Skript ausgeführt werden. Machen wir es einmal so.
Die Skripte erstellen und dann:
systemctl daemon-reload
systemctl enable --now pve-check-pbs.timer
Naja, hat jetzt noch nicht wirklich funktioniert:
root@backup:~# systemctl enable --now pve-check-pbs.timer
Created symlink /etc/systemd/system/timers.target.wants/pve-check-pbs.timer → /etc/systemd/system/pve-check-pbs.timer.
Failed to start pve-check-pbs.timer: Unit pve-check-pbs.timer has a bad unit file setting.
See system logs and 'systemctl status pve-check-pbs.timer' for details.
root@backup:~# systemctl status pve-check-pbs.timer
○ pve-check-pbs.timer - Periodic check for PBS VM shutdown
Loaded: bad-setting (Reason: Unit pve-check-pbs.timer has a bad unit file setting.)
Active: inactive (dead)
Trigger: n/a
Triggers: ● pve-check-pbs.service
May 01 20:25:48 backup systemd[1]: /etc/systemd/system/pve-check-pbs.timer:6: Failed to parse calendar specification, ignoring: *-*-* 00:05:00 # first shot>
May 01 20:25:48 backup systemd[1]: /etc/systemd/system/pve-check-pbs.timer:7: Failed to parse timer value, ignoring: 1min # then every minute
May 01 20:25:48 backup systemd[1]: pve-check-pbs.timer: Timer unit lacks value setting. Refusing.
Aha, der Fehler ist wohl der Kommentar in der selben Zeile, so müsste es funktionieren:
# /etc/systemd/system/pve-check-pbs.timer
[Unit]
Description=Periodic check for PBS VM shutdown
[Timer]
# First trigger at 00:05 each day
OnCalendar=*-*-* 00:05:00
# Then every 1 minute
OnUnitActiveSec=1min
# Don’t “catch up” missed calendar events
Persistent=false
[Install]
WantedBy=timers.target
Schaut besser aus:
Mit diesem Kurzbefehl kann man auch abfragen ob alles läuft:
systemctl list-timers --all | grep pve-check-pbs
Dann heißt es abwarten und schauen, ob sich der Server auch wirklich ausschaltet.
Fazit
Das Arbeiten mit Systemd ist wirklich eine angenehme Sache, man nimmt einfach ein beliebiges Skript, verpackt es schön in ein Systemd-Gewand und schon hat man einen Service, der sich wunderbar ins Linux-System einbettet. Hoffentlich hilft der Beitrag ja dem einen oder anderen.
Footnotes
-
Ursprünglich hat mir der Chatbot
Persistent=true
vorgeschlagen, das kann aber zu Problemen führen, wenn der Server einmal einen Tag nicht gelaufen ist. Siehe Nachbohren meinerseits:↩