# 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 drei Werkzeuge, um wieder **an die Daten im Snapshot** zu kommen: * `pvesnap-explorer` — zwei Fenster im Terminal wie im Midnight Commander * `pvesnap-web` — im Browser durchsehen und herunterladen, auch vom anderen Rechner * `pvesnap-recovery` — den Snapshot als **laufende Maschine** starten, abgeschottet ohne Netzwerk oder als vollwertige Wiederherstellung mit gleicher Identität --- ## Installation Auf dem Proxmox-Host als root: ```bash git clone 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 ` | Port der Web-Oberfläche | | `--bind ` | 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 | | `/usr/local/bin/pvesnap-recovery` | Snapshot als Maschine starten | | `/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:** ```bash ./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 ```bash pvesnap config ``` * **Gruppenliste** — `Enter` 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 ```ini [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: ```bash pvesnap check # Konfiguration prüfen systemctl reload pvesnap # Dienst übernimmt die Änderung ohne Neustart ``` ### Zeitplan Entweder ein Intervall … ```ini 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 ```ini 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: ```ini 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 ```ini 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 ```bash 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. --- ## 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-`, 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 mit `retry_delay` Abstand wiederholt. Echte Fehler (etwa „storage does not support snapshots") werden **nicht** wiederholt. * Mit `pause_between = 10s` lä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: ```bash 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: ```bash 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__` 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: ```bash pvesnap-explorer --cleanup ``` ### `pvesnap-explorer` — zwei Fenster im Terminal ```bash pvesnap-explorer # Gast und Snapshot auswählen pvesnap-explorer 802 # Snapshot von VM 802 auswählen pvesnap-explorer 802 # direkt öffnen pvesnap-explorer --local /srv/restore # rechtes Fenster startet dort pvesnap-explorer --path /mnt/x # beliebiges Verzeichnis (zum Ausprobieren) pvesnap-explorer --cleanup # hängengebliebene Einbindungen lösen ``` | Parameter | Vorgabe | Bedeutung | |---|---|---| | `vmid` | — | Gast direkt vorwählen | | `snapshot` | — | Snapshot direkt vorwählen | | `-l`, `--local ` | aktuelles Verzeichnis | Startverzeichnis im rechten Fenster | | `-p`, `--path ` | — | beliebiges Verzeichnis öffnen, ohne Proxmox zu fragen | | `--cleanup` | — | hängengebliebene Einbindungen lösen und beenden | | `-v`, `--verbose` | — | ausführliches Protokoll | 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 einrichten ```bash ./install.sh --with-webexplorer --port 8823 ``` Danach läuft die Oberfläche dauerhaft auf `http://:8823/` und startet mit dem System. Ohne `--port` bricht das Skript ab — der Port hat keine sinnvolle Vorgabe, die zu jedem Host passt. ```bash ./install.sh --with-webexplorer --port 8443 --bind 10.0.0.5 # nur auf einer IP ./install.sh --with-webexplorer --port 8823 --no-start # erst später starten ``` Alle Aufrufparameter des Dienstes stehen in **`/etc/pvesnap/web.conf`**: ``` PVESNAP_WEB_ARGS=--bind 0.0.0.0 --port 8823 --auth pve ``` Dort lässt sich alles nachträglich ändern — Port, Adresse, Anmeldeart, Rechte: ```bash nano /etc/pvesnap/web.conf systemctl restart pvesnap-web ``` Dienst verwalten: ```bash systemctl status pvesnap-web # läuft er? systemctl restart pvesnap-web # nach Änderungen in web.conf systemctl stop pvesnap-web # anhalten (hängt offene Snapshots aus) systemctl disable pvesnap-web # nicht mehr automatisch starten journalctl -u pvesnap-web -f # Protokoll, auch fehlgeschlagene Anmeldungen ``` #### Von Hand starten Für einen einmaligen Einsatz, ohne etwas zu installieren: ```bash pvesnap-web # Gast und Snapshot im Browser wählen pvesnap-web 802 # nur diesen Gast anbieten pvesnap-web 802 autosnap-taeglich-20260731-023000 # Snapshot gleich öffnen ``` | Parameter | Vorgabe | Bedeutung | |---|---|---| | `-P`, `--port ` | `8823` | Port, auf dem gelauscht wird | | `-b`, `--bind ` | `0.0.0.0` | Netzwerkadresse; `127.0.0.1` = nur lokal | | `-a`, `--auth pve\|token\|none` | `pve` | Anmeldeart, siehe unten | | `--require-privilege ` | `VM.Snapshot` | nötiges Recht auf dem Gast | | `--allow-user ` | — | dieser Benutzer darf alles (mehrfach möglich) | | `-t`, `--token ` | zufällig | Zugangsschlüssel selbst vorgeben | | `--no-token` | — | Kurzform für `--auth none` | | `-p`, `--path ` | — | beliebiges Verzeichnis statt eines Snapshots | | `--cleanup` | — | hängengebliebene Einbindungen lösen und beenden | | `-v`, `--verbose` | — | ausführliches Protokoll | | `-V`, `--version` | — | Version anzeigen | Beispiele: ```bash # Nur lokal lauschen, dazu vom Arbeitsplatz einen SSH-Tunnel legen pvesnap-web --bind 127.0.0.1 --port 8823 # am eigenen Rechner: ssh -L 8823:localhost:8823 root@pve # dann im Browser: http://localhost:8823/ # Ein Backup-Team soll ran, ohne Snapshot-Rechte zu haben pvesnap-web --port 8823 --require-privilege VM.Backup # Ein einzelner Dienstleister, unabhängig von den Proxmox-Rechten pvesnap-web --port 8823 --allow-user dienstleister@pve # Schnell etwas herausgeben, ohne Proxmox-Benutzer (Schlüssel in der Adresse) pvesnap-web --port 8823 --auth token # Ein beliebiges Verzeichnis freigeben, z. B. ein bereits gemountetes Backup pvesnap-web --path /mnt/restore --port 8823 # Nach einem Absturz aufräumen pvesnap-web --cleanup ``` Mit `pvesnap-web --help` gibt es dieselbe Übersicht im Terminal. **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/` 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 in `web.conf` `--bind 127.0.0.1` setzen. Beim Beenden (`Strg-C` oder `systemctl stop pvesnap-web`) wird ein offener Snapshot wieder ausgehängt. --- ## Einen Snapshot als Maschine starten — `pvesnap-recovery` Manchmal reicht es nicht, einzelne Dateien herauszukopieren. Eine Datenbank zum Beispiel liegt im Snapshot als Haufen halbfertiger Dateien — brauchbar wird sie erst, wenn der Datenbankserver läuft und selbst einen Dump schreibt. Genau dafür ist `pvesnap-recovery` da: Es startet den Snapshot als **eigenständige Maschine**, ohne das Original anzufassen. Zwei Betriebsarten: | | **live** | **recovery** | |---|---|---| | Netzwerk | keins | volles Netz | | Identität | bleibt (UUID, MACs, Hostname) | bleibt (UUID, MACs, Hostname) | | Wofür | hineinschauen, Dump ziehen, Daten holen | die Maschine wirklich wieder in Betrieb nehmen | | Risiko | keins — sie kann nichts erreichen | hoch, solange das Original läuft | Bedient wird das Ganze mit `pvesnap-recovery` (ncurses-Oberfläche mit Übersicht und Assistent) oder direkt über Parameter. ### Warum das schnell geht Die Datenträger werden nicht kopiert, sondern **geklont**. Auf Ceph/RBD, ZFS und LVM-thin ist das ein Copy-on-Write-Klon: Er ist in Sekunden fertig, egal wie groß die Platte ist, und belegt anfangs keinen zusätzlichen Platz. Eine 32-GB-Platte war im Test nach **0,8 Sekunden** geklont. Dafür wird `PVE::Storage::vdisk_clone` benutzt — dieselbe Funktion, die auch Proxmox selbst für Klone verwendet, samt Cluster-Sperre auf dem Storage. Es wird also nichts nachgebaut, was Proxmox schon kann. > Auf Datei-Storages (`dir`, `nfs`, `cifs`) geht das nicht — dort lässt sich aus > einem qcow2-internen Snapshot kein Klon ziehen. Für diese Storages bleibt der > Weg über `pvesnap-explorer`. ### Der Arbeitsspeicher kommt mit Enthält der Snapshot den Arbeitsspeicher (`vmstate`, in der Konfiguration `snapshot_vmstate = yes`), wird der mitgenommen. Die Maschine **bootet dann nicht**, sondern läuft genau dort weiter, wo sie beim Snapshot stand: * kein Crash-Recovery, kein `fsck`, kein Journal-Rollback * Datenbanken sind bereits offen und konsistent * die Uhr im Gast steht auf dem Snapshot-Zeitpunkt Im Test war das nachweisbar: Die Konsole zeigte 10:38 — die Uhrzeit des Snapshots — während auf dem Host längst 11:24 war. Damit das klappt, muss die Geräteausstattung exakt zum gespeicherten Zustand passen. `pvesnap-recovery` sorgt selbst dafür: * `vmgenid` und `smbios1` bleiben erhalten (fehlen sie, bricht das Laden mit `Unknown savevm section or instance 'vmgenid'` ab) * `runningmachine` und `runningcpu` aus dem Snapshot werden übernommen * die Netzwerkkarte bleibt **vorhanden**, wird aber abgeklemmt (`link_down=1`) statt entfernt — herausnehmen würde den Zustand unbrauchbar machen * das Austauschlaufwerk wird erst **nach** dem Fortsetzen angesteckt (Hotplug), weil der Gast in einem Zustand aufwacht, in dem es die Platte noch nicht gab Ein Detail, das Proxmox nicht selbst meldet: Schlägt das Laden des Arbeitsspeichers fehl, quittiert Proxmox den Start trotzdem mit `TASK OK` und lässt die Maschine angehalten stehen. `pvesnap-recovery` liest das Task-Protokoll mit, setzt die Maschine fort und sagt deutlich, wenn statt des RAM-Standes kalt gebootet wurde. Mit `--no-resume` lässt sich der Arbeitsspeicher bewusst weglassen. ### Daten aus einer Maschine ohne Netzwerk holen Ohne Netz kommt nichts heraus — dafür gibt es das **Austauschlaufwerk**. **Bei virtuellen Maschinen** wird eine leere, formatierte Zusatzplatte angelegt (Bezeichnung `PVESNAP`, mit einer kurzen `LIESMICH.txt` darauf): ```bash # Snapshot abgeschottet starten, mit 20 GB Austauschplatte pvesnap-recovery live 802 --exchange 20G # im Gast (über die noVNC-Konsole): mount /dev/disk/by-label/PVESNAP /mnt mysqldump --all-databases > /mnt/dump.sql umount /mnt && poweroff # auf dem Host: pvesnap-recovery pull 9802 # gibt den Pfad aus, z. B. /run/pvesnap/exchange/9802 pvesnap-recovery release 9802 # wieder lösen pvesnap-recovery destroy 9802 # alles verwerfen ``` **Bei Containern** ist es einfacher: Ein Host-Verzeichnis wird direkt durchgereicht und erscheint im Container unter `/mnt/pvesnap`. Was der Container hineinschreibt, liegt sofort auf dem Host — kein Ein- und Aushängen nötig. ```bash pvesnap-recovery live 100 --exchange ja pct exec 9100 -- sh -c 'pg_dumpall > /mnt/pvesnap/dump.sql' ls /var/lib/pvesnap/exchange/9100/ ``` Und hier passt auch die Idee mit dem **Netzlaufwerk**: Ist auf dem Host ohnehin schon eine NFS- oder CIFS-Freigabe eingehängt, lässt sie sich direkt durchreichen — der Container schreibt dann quasi aufs Netzlaufwerk, obwohl er selbst kein Netzwerk hat: ```bash pvesnap-recovery live 100 --exchange-dir /mnt/nfs/dumps ``` Bei virtuellen Maschinen geht das nicht: Ein Host-Verzeichnis lässt sich nicht in eine VM hineinreichen. Dort bleibt es bei der Austauschplatte. ### An die Konsole kommen ``` https://:8006/?console=kvm&novnc=1&vmid=9802&node=&resize=off&cmd= ``` Diese Adresse gibt `pvesnap-recovery` nach dem Anlegen aus, ebenso `pvesnap-recovery console `. Es ist die ganz normale noVNC-Konsole von Proxmox — die Maschine taucht auch in der Weboberfläche auf, markiert mit dem Tag `pvesnap-recovery`. Bei Containern geht zusätzlich `pct enter `. ### Wiederherstellung mit Netzwerk ```bash pvesnap-recovery recover 802 autosnap-g5min-20260731-103800 --newid 9802 ``` Hier bleibt alles erhalten, was die Maschine ausmacht: SMBIOS-UUID, `vmgenid`, MAC-Adressen, bei Containern der Hostname. Für alles im Netz ist sie dieselbe Maschine — inklusive Lizenzbindungen, AD-Mitgliedschaft und DHCP-Reservierungen. **Genau deshalb ist sie gefährlich.** `pvesnap-recovery` prüft vorher, ob das Original läuft, und warnt unmissverständlich. Zwei Maschinen mit derselben MAC und derselben IP im selben Netz geben Chaos. Das Original also **vorher stoppen** — oder mit `--net down` starten und die Karte erst zuschalten, wenn klar ist, dass die Luft rein ist. ### Parameter | Parameter | Bedeutung | |---|---| | `live [snapshot]` | abgeschottet starten (ohne Snapshot: der neueste) | | `recover [snapshot]` | mit Netzwerk und gleicher Identität starten | | `--newid ` | VMID der neuen Maschine (Vorgabe: nächste freie) | | `--node ` | auf welchem Node sie laufen soll | | `--net none\|down\|on` | keine Karte / Karte ohne Leitung / voll am Netz | | `--resume` / `--no-resume` | Arbeitsspeicher laden bzw. bewusst kalt starten | | `--exchange ` | Austauschplatte, z. B. `20G` (Container: `ja` genügt) | | `--exchange-storage ` | Storage dafür (Vorgabe: wie die erste Platte) | | `--exchange-fs ` | `ext4` (Vorgabe), `ext3`, `xfs`, `vfat`, `ntfs` | | `--exchange-dir ` | nur Container: dieses Host-Verzeichnis durchreichen | | `--memory `, `--cores ` | abweichende Ausstattung (nicht mit `--resume`) | | `--name ` | Name der neuen Maschine | | `--keep-binds` | durchgereichte Host-Verzeichnisse des Originals übernehmen | | `--no-start` | nur einrichten, nicht starten | | `-y`, `--yes` | nicht nachfragen | | Befehl | Bedeutung | |---|---| | `pvesnap-recovery` | ncurses-Oberfläche mit Übersicht und Assistent | | `list` | vorhandene Wiederherstellungen anzeigen | | `start` / `stop` | hoch- bzw. herunterfahren | | `console ` | noVNC-Adresse ausgeben | | `pull ` | Austauschlaufwerk auf den Host holen | | `release ` | es wieder lösen | | `destroy ` | restlos verwerfen | ### Was beim Verwerfen passiert `destroy` räumt vollständig auf: Maschine stoppen, Gast samt aller Klone entfernen, Austauschverzeichnis löschen — und den **Schutz der Quell-Snapshots wieder aufheben**. Dieser letzte Punkt ist wichtig: Ceph verlangt für einen Klon, dass der Quell-Snapshot geschützt ist (`rbd snap protect`), und Proxmox setzt das beim Klonen selbst. Bliebe der Schutz stehen, könnte die Vorhaltezeit diesen Snapshot später nicht mehr löschen — im Protokoll stünde dann immer wieder `snapshot is protected`. Solange eine Wiederherstellung existiert, ist ihr Quell-Snapshot also bewusst unlöschbar; danach nicht mehr. Zur Sicherheit fasst `destroy` nur Maschinen an, die den Tag `pvesnap-recovery` tragen. Eine von Hand angelegte VM lässt sich damit nicht versehentlich löschen. `uninstall.sh` warnt ebenfalls, wenn noch Wiederherstellungen offen sind. --- ## 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 recovery.py Snapshot als Maschine starten (Klone, Konfiguration, Aufräumen) recovery_ui.py ncurses-Oberfläche und Kommandozeile dazu 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.