Files
proxmox-snapshot-service/README.md
T
duffyduckandClaude Opus 5 8f1bf037cd TUI: Dienst per Taste 'r' neu laden
Nach dem Speichern musste man bisher in die Konsole wechseln, um die
Konfiguration mit "systemctl reload pvesnap" scharf zu schalten. Die
Gruppenliste kann das jetzt selbst:

* 'r' laedt den Dienst neu; ungespeicherte Aenderungen werden vorher zum
  Speichern angeboten, ein gestoppter Dienst zum Starten, und wenn das
  Neuladen scheitert, wird ein Neustart angeboten.
* Nach 's' fragt der Editor direkt, ob neu geladen werden soll.
* Die Kopfzeile zeigt laufend den Zustand des Dienstes.
* Die Tastenleiste bricht auf schmalen Terminals auf zwei Zeilen um,
  statt hinten abgeschnitten zu werden.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 01:47:04 +02:00

9.8 KiB

pvesnap — automatische Snapshots für Proxmox VE

Ein systemd-Dienst, der nach frei definierbaren Zeitplänen Snapshots von VMs und Containern anlegt und alte Snapshots nach einer einstellbaren Vorhaltezeit wieder löscht. Alles wird über eine INI-Datei gesteuert, die sich auch bequem in einem ncurses-Editor bearbeiten lässt.

  • Beliebig viele Gruppen — z. B. „diese VMs stündlich, jene täglich, der Rest monatlich“
  • VM-Auswahl nach VMID, Name (mit *-Mustern), Tag, Pool oder einfach „alle“
  • Vorhaltezeit je Gruppe nach Anzahl und/oder Alter
  • Jeder Snapshot bekommt eine Beschreibung, an der er sich wiedererkennen lässt
  • Läuft cluster-weit (alle Nodes) über pvesh
  • Nur Python-Standardbibliothek — keine zusätzlichen Pakete nötig

Installation

Auf dem Proxmox-Host als root:

git clone <dieses-repo> pvesnap && cd pvesnap
./install.sh

Das Skript legt an:

Pfad Inhalt
/usr/lib/pvesnap/ Programmcode
/usr/local/bin/pvesnap Startbefehl
/etc/pvesnap/pvesnap.conf Konfiguration (wird bei Updates nicht überschrieben)
/var/lib/pvesnap/state.json merkt sich die letzten Läufe
/etc/systemd/system/pvesnap.service systemd-Unit

In der mitgelieferten Beispielkonfiguration sind alle Gruppen auf enabled = no gesetzt — es passiert also erst etwas, wenn du eine Gruppe aktivierst.

Deinstallation:

./uninstall.sh            # Programm und Dienst entfernen, Konfiguration bleibt
./uninstall.sh --purge    # zusätzlich /etc/pvesnap und /var/lib/pvesnap löschen

Bereits angelegte Snapshots werden dabei nie angerührt.


Konfiguration im ncurses-Editor

pvesnap config
  • GruppenlisteEnter bearbeiten, n neu, c kopieren, d löschen, Leertaste an/aus, g globale Einstellungen, v Übersicht welche VM in welcher Gruppe landet, s speichern, r Dienst neu laden, q Ende
  • r — Dienst neu laden, ohne den Editor zu verlassen: entspricht systemctl reload pvesnap. Ungespeicherte Änderungen werden vorher zum Speichern angeboten, ein gestoppter Dienst zum Starten, und wenn das Neuladen scheitert, wird ein Neustart angeboten. Nach s fragt der Editor ohnehin gleich, ob neu geladen werden soll. Oben in der Kopfzeile steht jederzeit, ob der Dienst läuft. (Braucht root — also sudo pvesnap config.)
  • Gruppe bearbeiten — Feld auswählen, Enter ändern, Leertaste umschalten. Ganz unten steht live, auf wie viele Gäste die Auswahl gerade zutrifft.
  • VM-Auswahl (v) — Liste aller VMs und Container mit Node, Status und Tags; Leertaste auswählen, a alle, n keine, i umkehren, / filtern, Enter übernehmen.

Beim Speichern wird die INI-Datei neu geschrieben. Eigene Kommentare und ein [defaults]-Abschnitt gehen dabei verloren (die Werte bleiben inhaltlich erhalten, sie stehen danach in jeder Gruppe einzeln). Eine Sicherung wird als pvesnap.conf.bak abgelegt.


Konfiguration von Hand

[global]
prefix = auto                      # Namenspräfix aller pvesnap-Snapshots
check_interval = 60s               # wie oft der Dienst nach Fälligem schaut
state_file = /var/lib/pvesnap/state.json
log_level = INFO
task_timeout = 15m
run_on_start = no                  # beim Start sofort einen Durchlauf machen?
dry_run = no                       # yes = nichts wirklich tun, nur protokollieren
description = pvesnap | Gruppe: {group} | erstellt: {datetime} | Vorhaltezeit: {keep_time}

[defaults]                         # Vorgaben für alle Gruppen (optional)
skip_stopped = no

[group:stündlich]
interval = 1h
align = yes                        # an der Uhr ausrichten: 00:00, 01:00, ...
keep_count = 24                    # höchstens 24 Snapshots je VM
keep_time = 2d                     # nichts älter als 2 Tage
tags = stuendlich                  # alle VMs mit diesem Tag
skip_stopped = yes

[group:täglich]
schedule = daily
at = 02:30
keep_count = 14
keep_time = 21d
names = web-*, db-*

[group:monatlich]
schedule = monthly
day_of_month = 1
at = 04:00
keep_count = 6
all = yes
exclude_tags = nosnap

Nach jeder Änderung:

pvesnap check                # Konfiguration prüfen
systemctl reload pvesnap     # Dienst übernimmt die Änderung ohne Neustart

Zeitplan

Entweder ein Intervall …

interval = 30m      # 30m, 1h, 6h, 2d12h, 1w ...
align = yes         # yes: an der Uhr ausgerichtet (00:00, 00:30, 01:00, ...)
                    # no:  30 Minuten nach dem letzten Lauf

… oder ein fester Termin:

schedule zusätzliche Angaben Beispiel
hourly minute = 15 jede Stunde um :15
daily at = 02:30 täglich um 02:30
weekly at, day_of_week = so sonntags um 03:00
monthly at, day_of_month = 1 am 1. jedes Monats
yearly at, day_of_month, month einmal jährlich

day_of_month = 31 wird in kürzeren Monaten automatisch auf den letzten Tag gezogen. Läuft der Host zum Termin nicht, wird der Lauf beim nächsten Start nachgeholt.

Vorhaltezeit

keep_count = 24     # höchstens 24 Snapshots je VM und Gruppe (0 = unbegrenzt)
keep_time  = 7d     # nichts älter als 7 Tage           (0 = unbegrenzt)
keep_min   = 1      # so viele bleiben in jedem Fall stehen

Beides ist kombinierbar — gelöscht wird, was eine der beiden Grenzen reißt. Mindestens eine der beiden Angaben muss gesetzt sein, sonst würde die Zahl der Snapshots unbegrenzt wachsen (pvesnap check weist darauf hin).

Welche VMs?

Alle Angaben wirken als ODER, Ausschlüsse gewinnen immer:

all = yes                     # alle VMs und Container
vmids = 100,101,105-110       # nach ID, auch Bereiche
names = web-*, db-0?          # nach Name, mit Platzhaltern
tags = prod, wichtig          # nach Proxmox-Tag
pools = Kunden                # nach Proxmox-Pool
types = qemu                  # nur VMs (bzw. lxc = nur Container; leer = beides)

exclude_vmids = 999
exclude_names = *-test
exclude_tags = nosnap

Snapshot-Optionen

vmstate = yes         # RAM mitsichern (nur QEMU und nur bei laufender VM)
skip_stopped = yes    # gestoppte Gäste überspringen
description = Sicherung von {name} ({vmid}) vom {date}

Platzhalter der Beschreibung: {group} {group_slug} {vmid} {name} {node} {type} {pool} {tags} {date} {time} {datetime} {timestamp} {keep_time} {keep_count} {schedule}


Namensschema und Sicherheit

auto-taeglich-20260730-023000
 │      │         │       └── Uhrzeit
 │      │         └────────── Datum
 │      └──────────────────── Kurzname der Gruppe
 └─────────────────────────── prefix aus [global]

pvesnap löscht ausschließlich Snapshots, deren Name exakt auf dieses Muster passt und deren Gruppen-Kurzname zur jeweiligen Gruppe gehört. Von Hand oder von anderen Werkzeugen angelegte Snapshots bleiben garantiert unangetastet.

Umlaute und Sonderzeichen im Gruppennamen werden für den Kurznamen umgeschrieben (täglichtaeglich). Wenn zwei Gruppen denselben Kurznamen ergäben, meldet das pvesnap check.


Befehle

pvesnap status                    # Übersicht: Gruppen, letzte und nächste Läufe
pvesnap vms                       # alle VMs und die Gruppen, in denen sie stecken
pvesnap list                      # vorhandene pvesnap-Snapshots  (-a = auch fremde)
pvesnap check                     # Konfiguration prüfen
pvesnap config                    # ncurses-Editor

pvesnap run                       # jetzt fällige Gruppen ausführen
pvesnap run --force -g täglich    # eine Gruppe sofort ausführen
pvesnap run --force --dry-run     # Probelauf, ändert nichts
pvesnap prune -g stündlich        # nur aufräumen, keine neuen Snapshots

systemctl status pvesnap
systemctl reload pvesnap          # Konfiguration neu einlesen (SIGHUP)
journalctl -u pvesnap -f          # Protokoll mitlesen

Ein manueller pvesnap run ist auch möglich, während der Dienst läuft — beide teilen sich eine Sperre und kommen sich nicht in die Quere.

Mit -c /pfad/zur.conf lässt sich jederzeit eine andere Konfiguration verwenden, z. B. zum Ausprobieren.


Hinweise aus der Praxis

  • Snapshot ist kein Backup. Snapshots liegen auf demselben Storage wie die VM. Für echte Sicherungen zusätzlich vzdump / Proxmox Backup Server verwenden.
  • Speicherplatz: Jeder Snapshot hält alte Blöcke fest. Bei schreibfreudigen VMs lieber kurze Vorhaltezeiten wählen und den Storage im Auge behalten.
  • Nicht jedes Storage kann Snapshots — LVM-thick und Verzeichnis-Storage mit raw-Images können es nicht. Solche VMs melden einen Fehler im Protokoll; die übrigen laufen normal weiter.
  • vmstate = yes friert die VM kurz ein und braucht Platz in Höhe des zugewiesenen RAM. Für regelmäßige Läufe meist unnötig.
  • Gruppe umbenennen: Der neue Name bekommt einen eigenen Kurznamen. Snapshots unter dem alten Namen werden dann nicht mehr automatisch aufgeräumt — vorher aufräumen lassen oder die alten Snapshots von Hand entfernen.
  • Zeitumstellung: Es wird mit lokaler Zeit gerechnet. Ein täglicher Termin um 02:30 kann in der Umstellungsnacht ausfallen oder doppelt anstehen.

Aufbau

pvesnap/
  config.py     INI lesen und schreiben, Gruppen- und Globaleinstellungen
  schedule.py   Berechnung des nächsten Termins
  naming.py     Namensschema und Beschreibungs-Vorlagen
  proxmox.py    pvesh-Anbindung (Inventar, Snapshots anlegen/löschen)
  engine.py     Auswahl der Gäste, Anlegen, Aufräumen
  daemon.py     Hauptschleife, Signale, Sperren
  state.py      merkt sich die letzten Läufe
  tui.py        ncurses-Editor
  cli.py        Kommandozeile
config/pvesnap.conf.example
systemd/pvesnap.service
install.sh  uninstall.sh