Zwei gemeldete Fehler.
1. Im rechten Fenster des Explorers liessen sich weder Verzeichnisse
oeffnen noch ".." benutzen. Die Pruefung "bleibt der Pfad innerhalb der
Wurzel?" haengte an die Wurzel ein "/" an - bei der Wurzel "/" des
lokalen Fensters wurde daraus "//", worauf kein Pfad passt. Damit gab
open_current() immer False zurueck. Die Pruefung steckt jetzt in
within() und behandelt diesen Fall; die Web-Oberflaeche benutzt
dieselbe Funktion.
2. "Snapshots vom LXC aufrufen geht nicht": der Container hatte gar keine
brauchbaren Snapshots. In seiner Konfiguration standen nur zwei
Eintraege mit snapstate "prepare" und "delete" - Reste aus der Zeit, in
der jeder Snapshot am cfs-Lock scheiterte. Auf dem Storage liegt
dahinter nichts (rbd snap ls ist leer), oeffnen kann man sie also
nicht.
Solche Eintraege werden jetzt als das behandelt, was sie sind:
* Explorer und Web-Oberflaeche bieten sie nicht mehr zum Oeffnen an
und nennen den Aufraeumbefehl.
* "pvesnap list" markiert sie mit "!".
* Beim Aufraeumen entfernt der Dienst sie zuerst, und zwar mit
--force, weil sich ein Eintrag ohne Storage-Snapshot sonst nicht
loeschen laesst. Vorher waeren sie ewig liegen geblieben und haetten
zusaetzlich die Zahl der behaltenen Snapshots verfaelscht.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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
Dazu zwei Werkzeuge, um wieder an die Daten im Snapshot zu kommen:
pvesnap-explorer— zwei Fenster im Terminal wie im Midnight Commanderpvesnap-web— im Browser durchsehen und herunterladen, auch vom anderen Rechner
Installation
Auf dem Proxmox-Host als root:
git clone <dieses-repo> pvesnap && cd pvesnap
./install.sh # nur der Snapshot-Dienst
./install.sh --with-webexplorer --port 8823 # zusätzlich die Web-Oberfläche
./install.sh --help # alle Optionen
| Option | |
|---|---|
--with-webexplorer |
Web-Oberfläche als Dienst einrichten (erfordert --port) |
--port <nummer> |
Port der Web-Oberfläche |
--bind <adresse> |
Adresse, auf der sie lauscht (Vorgabe 0.0.0.0) |
--no-start |
installieren, aber Dienste nicht starten |
--force |
auch ohne erkanntes Proxmox VE installieren |
Das Skript legt an:
| Pfad | Inhalt |
|---|---|
/usr/lib/pvesnap/ |
Programmcode |
/usr/local/bin/pvesnap |
Dienst und Konfiguration |
/usr/local/bin/pvesnap-explorer |
Zwei-Fenster-Explorer |
/usr/local/bin/pvesnap-web |
Web-Oberfläche |
/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 des Snapshot-Dienstes |
/etc/systemd/system/pvesnap-web.service |
Unit der Web-Oberfläche (nur mit --with-webexplorer) |
/etc/pvesnap/web.conf |
Port und Anmeldeart der Web-Oberfläche |
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
- Gruppenliste —
Enterbearbeiten,nneu,ckopieren,dlöschen,Leertastean/aus,gglobale Einstellungen,vÜbersicht welche VM in welcher Gruppe landet,sspeichern,rDienst neu laden,qEnde r— Dienst neu laden, ohne den Editor zu verlassen: entsprichtsystemctl reload pvesnap. Ungespeicherte Änderungen werden vorher zum Speichern angeboten, ein gestoppter Dienst zum Starten, und wenn das Neuladen scheitert, wird ein Neustart angeboten. Nachsfragt der Editor ohnehin gleich, ob neu geladen werden soll. Oben in der Kopfzeile steht jederzeit, ob der Dienst läuft. (Braucht root — alsosudo pvesnap config.)- Gruppe bearbeiten — Feld auswählen,
Enterändern,Leertasteumschalten. 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;Leertasteauswählen,aalle,nkeine,iumkehren,/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 alspvesnap.conf.bakabgelegt.
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
retries = 2 # Wiederholungen, wenn eine Sperre belegt ist
retry_delay = 60s
pause_between = 0s # Pause zwischen zwei Gästen
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äglich → taeglich). 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 = yesfriert 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.
Wenn Snapshots am Storage-Lock scheitern
trying to acquire cfs lock 'storage-data' ...
TASK ERROR: cfs-lock 'storage-data' error: got lock request timeout
storage-data ist dabei kein falsch gelesener Name — in pmxcfs heißen
Storage-Sperren immer storage-<name>, gemeint ist also das Storage data.
Proxmox nimmt diese Sperre beim Anlegen eines Snapshots und gibt nach 60 s auf,
wenn sie jemand anderes hält.
pvesnap geht damit so um:
- Der Aufruf wartet, bis der Snapshot-Task in Proxmox wirklich fertig ist, bevor die nächste VM drankommt — sonst würden sich die Läufe gegenseitig aussperren.
- Sperr-Fehler gelten als vorübergehend und werden
retries-mal mitretry_delayAbstand wiederholt. Echte Fehler (etwa „storage does not support snapshots") werden nicht wiederholt. - Mit
pause_between = 10slässt sich zusätzlich Druck vom Storage nehmen, wenn viele VMs auf demselben Storage liegen.
Der Dienst scheitert, von Hand geht es
Scheitert jeder Lauf des Dienstes am Storage-Lock, während derselbe Befehl
in der Shell funktioniert, liegt es an der Umgebung des Dienstes — nicht am
Storage. pvesh führt die Proxmox-API im eigenen Prozess aus, der Snapshot-Task
ist also ein Kindprozess von pvesnap und erbt alles, was in der systemd-Unit
eingeschränkt wurde.
Der Klassiker ist ProtectSystem= in der Unit: das hängt /etc schreibgeschützt
ein, und pmxcfs legt seine Sperren als Verzeichnisse unter /etc/pve/priv/lock/
an. Das mkdir scheitert, Proxmox wiederholt es erfolglos — und meldet am Ende
einen Lock-Timeout statt eines Rechtefehlers. Prüfen:
systemctl show pvesnap -p ProtectSystem -p ProtectHome -p PrivateTmp -p ReadOnlyPaths
pvesnap check # meldet so etwas von sich aus
Alle drei müssen leer bzw. no sein. Die mitgelieferte Unit enthält deshalb
bewusst keine Sandbox-Optionen.
Wenn die Sperre wirklich belegt ist
Diese Kommandos helfen beim Eingrenzen:
pvesm status # ist das Storage online und erreichbar?
grep -A6 "^[a-z]*: data" /etc/pve/storage.cfg
pvesh get /cluster/tasks --output-format json | head # hängt noch ein Task?
systemctl status pvestatd pve-cluster
journalctl -u pvestatd -n 50
Häufigste Ursachen: ein hängender Backup- oder Replikationsjob auf demselben
Storage, ein nicht erreichbares NFS/CIFS-Storage (dann blockiert pvestatd),
oder ein abgebrochener Task, der die Sperre nicht freigegeben hat.
Dateien aus einem Snapshot holen
Ein Snapshot ist kein Verzeichnis. Damit man hineinschauen kann, wird er schreibgeschützt als Blockgerät eingebunden und gemountet — je nach Storage unterschiedlich:
| Storage | Weg hinein |
|---|---|
rbd (Ceph) |
rbd map pool/image@snap, bei nicht unterstützten Image-Features rbd-nbd |
zfspool |
Container direkt über .zfs/snapshot/…, VMs über einen temporären Klon |
lvmthin, lvm |
die Snapshot-LV snap_<volume>_<snapname> aktivieren |
dir, nfs, cifs |
qemu-nbd --load-snapshot (nur qcow2) |
Danach werden Partitionen erkannt und mit ro,noload bzw. ro,norecovery,nouuid
gemountet — Snapshots laufender Gäste haben fast immer ein unsauberes Journal,
ohne diese Optionen würde der Kernel hineinschreiben wollen.
Aufgeräumt wird beim Beenden automatisch. Bleibt nach einem Absturz etwas hängen:
pvesnap-explorer --cleanup
pvesnap-explorer — zwei Fenster im Terminal
pvesnap-explorer # Gast und Snapshot auswählen
pvesnap-explorer 802 # Snapshot von VM 802 auswählen
pvesnap-explorer 802 <snapname> # direkt öffnen
pvesnap-explorer --path /mnt/x # beliebiges Verzeichnis (zum Ausprobieren)
Links der Snapshot, rechts der lokale Rechner:
| Taste | |
|---|---|
Tab |
Fenster wechseln |
Pfeiltasten, Enter |
bewegen, Verzeichnis öffnen oder Datei ansehen |
Backspace |
ein Verzeichnis hoch |
Leertaste |
markieren; * umkehren, a alle, u keine |
F5 / c |
Markiertes ins andere Fenster kopieren |
F3 / v |
Datei ansehen (Text oder Hex) |
F7 / n |
neues Verzeichnis (nur lokal) |
F6 / g |
Verzeichnis direkt eingeben |
F2 / m |
anderes Dateisystem des Snapshots wählen |
F10 / q |
eine Ebene zurück (Snapshot wird ausgehängt) |
Der Explorer hat drei Ebenen: Gastauswahl → Snapshot-Auswahl → Dateien. Mit
q geht es jeweils eine Ebene zurück; beim Verlassen des Dateibrowsers wird der
Snapshot wieder ausgehängt, sodass man gleich den nächsten öffnen kann. Das
rechte Fenster behält dabei sein Verzeichnis — praktisch, wenn man aus mehreren
Snapshots ins selbe Zielverzeichnis sammelt.
Kopiert wird immer vom aktiven ins andere Fenster — in den Snapshot hinein geht
nichts, er ist schreibgeschützt. Beim Kopieren gibt es einen Fortschrittsbalken,
ESC bricht ab; vorhandene Dateien werden abgefragt (ja / nein / alle /
keine). Gerätedateien und Sockets werden übersprungen, symbolische Verweise
bleiben Verweise.
pvesnap-web — im Browser herunterladen
Als Dienst (empfohlen, überlebt Neustarts):
./install.sh --with-webexplorer --port 8823
Danach läuft die Oberfläche dauerhaft auf http://<host>:8823/. Port, Adresse
und Anmeldeart stehen in /etc/pvesnap/web.conf:
PVESNAP_WEB_ARGS=--bind 0.0.0.0 --port 8823 --auth pve
Nach Änderungen systemctl restart pvesnap-web. Oder von Hand für einen
einzelnen Einsatz:
pvesnap-web # Gast und Snapshot im Browser wählen
pvesnap-web 802 <snapname> # diesen Snapshot gleich öffnen
pvesnap-web --path /verzeichnis # beliebiges Verzeichnis (ohne Proxmox)
Anmeldung mit den Proxmox-Benutzern. Die Oberfläche zeigt dieselbe
Anmeldemaske wie Proxmox — Benutzername, Passwort, Realm-Auswahl. Geprüft wird
über POST /access/ticket auf der lokalen Proxmox-API, also genau den Weg, den
auch das Proxmox-Webinterface geht. Alle Realms (pam, pve, LDAP, AD …)
funktionieren damit automatisch. Zwei-Faktor-Anmeldungen werden abgewiesen statt
halb durchgewinkt.
Wer darf was sehen? Ein gültiges Passwort allein reicht nicht — sonst könnte
jeder Proxmox-Benutzer sämtliche Dateien aller Gäste lesen. Zusätzlich muss der
Benutzer auf dem jeweiligen Gast das Recht VM.Snapshot besitzen (geprüft
auf /, /vms, /vms/<id> und dem Pool des Gastes). In der Gastliste tauchen
nur Gäste auf, für die das zutrifft; root@pam sieht wie in Proxmox alles.
Anpassen mit:
--require-privilege VM.Backup # anderes Recht verlangen
--allow-user backup@pve # einzelne Benutzer generell zulassen
--auth token # stattdessen Schlüssel in der Adresse
--auth none # ohne Anmeldung (nur im vertrauten Netz)
Ohne Proxmox (--path) gibt es keine Benutzer — dort wird automatisch ein
Zugangsschlüssel erzeugt und beim Start als Adresse ausgegeben.
Bedienung: Snapshot anklicken, durchklicken; einzelne Dateien werden direkt heruntergeladen, Verzeichnisse und Mehrfachauswahl als ZIP (im Strom erzeugt, es wird nichts zwischengespeichert).
Zur Sicherheit:
- Es wird ausschließlich gelesen. Die Oberfläche kann nichts ändern oder löschen.
- Pfade außerhalb des eingebundenen Snapshots werden abgewiesen — über die Adresszeile kommt man nicht an den Rest des Hosts.
- Fehlgeschlagene Anmeldungen landen mit Absender-IP im Journal
(
journalctl -u pvesnap-web). - Die Verbindung ist unverschlüsseltes HTTP — Passwörter gehen im Klartext über
das Netz. Über unsichere Netze deshalb besser einen SSH-Tunnel legen:
ssh -L 8823:localhost:8823 root@pve, dazu inweb.conf--bind 127.0.0.1setzen.
Beim Beenden (Strg-C oder systemctl stop pvesnap-web) wird ein offener
Snapshot wieder ausgehängt.
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
preflight.py prüft die Umgebung (root, /etc/pve beschreibbar, /root)
cli.py Kommandozeile
curses_util.py gemeinsame curses-Bausteine
tui.py ncurses-Konfigurationseditor
snapfs.py Snapshots einbinden und mounten (rbd/zfs/lvm/qcow2)
explorer.py Zwei-Fenster-Explorer im Terminal
web/ Web-Oberfläche zum Herunterladen
server.py HTTP-Server, ZIP im Strom, Zugangsschlüssel
assets.py Stylesheet und HTML-Gerüst
config/pvesnap.conf.example
systemd/pvesnap.service
install.sh uninstall.sh
Der Dienst (pvesnap) und die beiden Explorer teilen sich proxmox.py;
snapfs.py liefert beiden Oberflächen einen ganz gewöhnlichen Pfad, sodass
Explorer und Web-Oberfläche nichts über Ceph, ZFS oder LVM wissen müssen.