Files
proxmox-snapshot-service/README.md
T
duffyduckandClaude Opus 5 725fbf8732 pvesnap-recovery: Snapshots als Maschine starten
Zwei Betriebsarten:

  live      abgeschottet ohne Netzwerk - zum Hineinschauen ueber die
            noVNC-Konsole, Dump ziehen, Daten ueber ein Austauschlaufwerk
            herausholen
  recovery  mit Netzwerk und gleicher Identitaet (SMBIOS-UUID, vmgenid,
            MAC-Adressen, Hostname) - warnt, wenn das Original noch laeuft

Die Datentraeger werden ueber PVE::Storage::vdisk_clone geklont, also mit
derselben Funktion und Cluster-Sperre, die Proxmox selbst verwendet. Auf
Ceph/ZFS/LVM-thin ist das Copy-on-Write: 32 GiB waren im Test in 0,8s
geklont, das Original bleibt unberuehrt.

Enthaelt der Snapshot den Arbeitsspeicher, wird er mitkopiert - die Maschine
laeuft dann genau dort weiter, wo sie stand, statt zu booten. Dafuer muss die
Geraeteausstattung exakt passen:

  - vmgenid und smbios1 bleiben erhalten (ohne vmgenid bricht das Laden mit
    "Unknown savevm section" ab)
  - runningmachine/runningcpu werden uebernommen
  - die Netzwerkkarte wird abgeklemmt statt entfernt
  - das Austauschlaufwerk kommt erst nach dem Fortsetzen per Hotplug dazu,
    sonst bemerkt der Gast es nie (nachgewiesen ueber "info virtio-status")

Proxmox meldet einen fehlgeschlagenen RAM-Ladevorgang als TASK OK und laesst
die Maschine angehalten stehen - deshalb wird das Task-Protokoll mitgelesen,
fortgesetzt und deutlich gemeldet, wenn stattdessen kalt gebootet wurde.

Austausch mit dem Host: bei VMs eine formatierte Zusatzplatte, bei
Containern ein durchgereichtes Verzeichnis - dort passt auch ein bereits
eingehaengtes Netzlaufwerk.

Verworfen wird restlos, samt Aufheben des rbd-Snapshot-Schutzes, den das
Klonen setzt - sonst koennte die Vorhaltezeit den Snapshot nie mehr loeschen.
Angefasst wird nur, was den Tag pvesnap-recovery traegt.

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

769 lines
31 KiB
Markdown

# 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 <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 |
| `/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-<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 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_<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:
```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 <snapname> # 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 <pfad>` | aktuelles Verzeichnis | Startverzeichnis im rechten Fenster |
| `-p`, `--path <pfad>` | — | 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://<host>: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 <nummer>` | `8823` | Port, auf dem gelauscht wird |
| `-b`, `--bind <adresse>` | `0.0.0.0` | Netzwerkadresse; `127.0.0.1` = nur lokal |
| `-a`, `--auth pve\|token\|none` | `pve` | Anmeldeart, siehe unten |
| `--require-privilege <recht>` | `VM.Snapshot` | nötiges Recht auf dem Gast |
| `--allow-user <user@realm>` | — | dieser Benutzer darf alles (mehrfach möglich) |
| `-t`, `--token <schlüssel>` | zufällig | Zugangsschlüssel selbst vorgeben |
| `--no-token` | — | Kurzform für `--auth none` |
| `-p`, `--path <verzeichnis>` | — | 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/<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 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://<node>:8006/?console=kvm&novnc=1&vmid=9802&node=<node>&resize=off&cmd=
```
Diese Adresse gibt `pvesnap-recovery` nach dem Anlegen aus, ebenso
`pvesnap-recovery console <VMID>`. 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 <VMID>`.
### 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 <VMID> [snapshot]` | abgeschottet starten (ohne Snapshot: der neueste) |
| `recover <VMID> [snapshot]` | mit Netzwerk und gleicher Identität starten |
| `--newid <ID>` | VMID der neuen Maschine (Vorgabe: nächste freie) |
| `--node <name>` | 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 <größe>` | Austauschplatte, z. B. `20G` (Container: `ja` genügt) |
| `--exchange-storage <name>` | Storage dafür (Vorgabe: wie die erste Platte) |
| `--exchange-fs <fs>` | `ext4` (Vorgabe), `ext3`, `xfs`, `vfat`, `ntfs` |
| `--exchange-dir <pfad>` | nur Container: dieses Host-Verzeichnis durchreichen |
| `--memory <MB>`, `--cores <n>` | abweichende Ausstattung (nicht mit `--resume`) |
| `--name <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 <VMID>` | noVNC-Adresse ausgeben |
| `pull <VMID>` | Austauschlaufwerk auf den Host holen |
| `release <VMID>` | es wieder lösen |
| `destroy <VMID>` | 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.