Table of contents
- Backup & Restore — Laufzeit-Zustand (PVE-LXCs + VPS-Guacamole-Stack)
- 1. PVE-Host: Backup-Dataset + Ausschluss der NAS-Freigabe
- 2. PVE-Host: nächtlicher vzdump für alle Instanzen
- 3. VPS: rsync/cron nachinstallieren
- 4. VPS: Backup-Skript für die Guacamole-Stack
- 5. Restriktiver Pull-Key (VPS → NAS)
- 6. PVE-Host: Pull direkt im Anschluss an vzdump, kein eigener Cron-Eintrag
- 7. Restore
- Referenz: Backup-Übersicht
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Backup & Restore — Laufzeit-Zustand (PVE-LXCs + VPS-Guacamole-Stack)
Sichert den Laufzeit-Zustand, nicht nur Dateien: LXC-Configs, Datenbanken, Guacamole-Verbindungsdefinitionen/TOTP-Secrets, Vaultwardens verschlüsselten Tresor. Der ZFS-Mirror (nas/freigabe) deckt reinen Dateiverlust ab — dieses Kapitel deckt den Fall "Server/Container ist weg und muss aus dem Nichts wiederhergestellt werden".
1. PVE-Host: Backup-Dataset + Ausschluss der NAS-Freigabe
zfs create nas/backups
mkdir -p /nas/backups/pve /nas/backups/vps
pct set 101 -mp0 /nas/freigabe,mp=/srv/freigabe,backup=0
nas/backups bewusst nicht als PVE-Storage registrieren (gleiche Begründung wie beim nas-Pool selbst, siehe 01-pve-host.md Abschnitt 3) — vzdump --dumpdir schreibt direkt dorthin, kein pvestatd-Polling nötig. CT101s mp0 (Bind-Mount von /nas/freigabe, 37GB) wird von Container-Backups ausgeschlossen, sonst läge die komplette NAS-Freigabe redundant auch im Backup-Ziel auf demselben Pool.
2. PVE-Host: nächtlicher vzdump für alle Instanzen
⚠️ Diese Sektion ist die häufigste Stelle, an der die Doku hinter der Realität zurückbleibt — jede neue LXC/VM braucht einen Nachtrag hier, sonst fehlt sie stillschweigend im Backup. Stand halten heißt: bei jedem neuen Container/jeder neuen VM prüfen, ob diese Sektion noch stimmt.
Läuft nicht mehr als roher Crontab-Einzeiler, sondern über ein versioniertes Skript
(scripts/vzdump-backup.sh, ausgerollt nach /usr/local/bin/) mit ntfy-Erfolgs-/Fehlschlag-Meldung:
(crontab -l 2>/dev/null; echo "0 3 * * * /usr/local/bin/vzdump-backup.sh") | crontab -
vzdump-backup.sh (Kern-Aufruf, aktueller Stand — IDs bei jeder neuen Instanz hier
nachziehen, siehe Warnung oben):
vzdump 100 101 102 103 104 105 106 200 --mode snapshot --compress zstd --dumpdir /nas/backups/pve --prune-backups keep-last=3
LVM-Thin-Snapshot-Modus (--mode snapshot) → alle Instanzen laufen auf local-lvm, minimale
Downtime. Vaultwarden (CT103, SQLite im WAL-Modus) ist damit ohne separates DB-Skript
crash-konsistent abgedeckt — SQLite ist genau für diesen Fall ausgelegt. VM 200 (QEMU, nicht LXC)
läuft im selben vzdump-Aufruf mit, Snapshot-Modus funktioniert für beide Typen identisch.
3. VPS: rsync/cron nachinstallieren
apt update && apt install -y rsync cron
systemctl enable --now cron
⚠️ NICHT vergessen: Die minimale Debian-<DEBIAN_RELEASE>-Installation auf dem VPS hat weder rsync (liefert rrsync mit, ab Version 3.4.x fertig unter /usr/bin/rrsync) noch cron vorinstalliert. Ohne cron-Paket schlägt crontab -l/crontab - mit "command not found" fehl.
4. VPS: Backup-Skript für die Guacamole-Stack
/usr/local/bin/backup-guacamole.sh:
#!/bin/bash
set -e
DEST=/root/backups
DATE=$(date +%F)
mkdir -p "$DEST"
docker exec guac-postgres pg_dump -U guacamole_user guacamole_db | gzip > "$DEST/guacamole_db_$DATE.sql.gz"
tar czf "$DEST/guacamole-opt_$DATE.tar.gz" --exclude=pg-data -C /opt guacamole
find "$DEST" -mtime +7 -delete
chmod +x /usr/local/bin/backup-guacamole.sh
(crontab -l 2>/dev/null; echo "0 2 * * * /usr/local/bin/backup-guacamole.sh >> /var/log/backup-guacamole.log 2>&1") | crontab -
pg_dump statt rohem Kopieren von pg-data — transaktionssicher bei laufendem Postgres, roher Kopiervorgang eines laufenden Datenverzeichnisses wäre das nicht. Rotation: 7 Tage lokal auf dem VPS (das eigentliche Archiv liegt nach Schritt 6 auf dem NAS).
5. Restriktiver Pull-Key (VPS → NAS)
Dediziertes Keypair auf dem PVE-Host, nicht id_rsa oder wol_vps_key wiederverwenden:
ssh-keygen -t ed25519 -f /root/.ssh/backup_pull_key -N "" -C "backup-pull@pve"
Public Key auf dem VPS in authorized_keys ergänzen:
PUBKEY=$(cat /root/.ssh/backup_pull_key.pub)
ssh root@<TS_IP_VPS> "echo 'restrict,command=\"rrsync -ro /root/backups/\" $PUBKEY' >> /root/.ssh/authorized_keys"
→ Der Key kann ausschließlich lesend aus /root/backups/ syncen, keine Shell, kein Port-/Agent-Forwarding. Im Gegensatz zum wolonly-Key (01-pve-host.md Abschnitt 6) ist restrict hier unproblematisch — der Pull läuft per Cron ohne PTY-Anforderung, das PTY-Problem betraf nur Guacamoles SSH-Client.
6. PVE-Host: Pull direkt im Anschluss an vzdump, kein eigener Cron-Eintrag
vps-backup-pull.sh (der rsync-Pull) wird nicht über einen eigenen Cron-Zeitpunkt getriggert, sondern am Ende von vzdump-backup.sh aufgerufen — beide laufen damit in einem einzigen HDD-Wachfenster:
# letzte Zeile in /usr/local/bin/vzdump-backup.sh:
/usr/local/bin/vps-backup-pull.sh
⚠️ NICHT TUN: vzdump-backup.sh (03:00) und vps-backup-pull.sh als zwei getrennte Cron-Einträge eine Stunde auseinander laufen lassen (ursprünglich als zwei getrennte Zeitpunkte gesetzt). Die NAS-HDDs gehen nach dem vzdump-Snapshot in ~5 Minuten wieder in Standby (Spindown-Timeout) — der komplette vzdump-Lauf dauert aber nur ~2 Minuten. Ein zweiter Cron-Job eine Stunde später weckt die Platten dadurch ein zweites Mal unnötig. Fix: vps-backup-pull.sh als letzten Schritt in vzdump-backup.sh aufrufen (kein eigener Cron-Eintrag mehr) — beide Backups passen zeitlich locker in ein gemeinsames Wachfenster.
⚠️ NICHT TUN: Auf dem Client den vollen Pfad wiederholen (root@vps:/root/backups/ ziel/). rrsync -ro /root/backups/ fixiert das Restricted-Verzeichnis bereits serverseitig; ein vom Client mitgegebener Pfad wird (nach Entfernen des führenden /) daran angehängt — Ergebnis ist der doppelte, nicht existierende Pfad /root/backups/root/backups/ (Fehler "No such file or directory"). Korrekt ist ein leerer Pfad nach dem Doppelpunkt: root@<TS_IP_VPS>: — das adressiert die Wurzel des Restricted-Verzeichnisses.
7. Restore
LXC (jede der sieben LXC-Instanzen: 100–106):
pct restore <vmid> /nas/backups/pve/vzdump-lxc-<vmid>-<timestamp>.tar.zst --storage local-lvm
Vaultwarden (CT103) braucht keinen separaten Restore-Schritt — Tresor + rsa_key.pem sind im LXC-Restore enthalten. Weitere Dienste mit eigener DB im Container-Rootfs ebenso: alle Zustandsdaten liegen im Container selbst, keine externe DB.
VM (200, QEMU statt LXC — anderer Restore-Befehl):
qmrestore /nas/backups/pve/vzdump-qemu-200-<timestamp>.vma.zst 200 --storage local-lvm
Guacamole-Stack (VPS): Compose-Stack aus dem Tar-Archiv wiederherstellen, dann DB einspielen:
gunzip -c guacamole-opt_<date>.tar.gz | tar x -C /opt # falls /opt/guacamole fehlt
cd /opt/guacamole && docker compose up -d postgres guacd
gunzip -c guacamole_db_<date>.sql.gz | docker exec -i guac-postgres psql -U guacamole_user guacamole_db
docker compose up -d guacamole
Referenz: Backup-Übersicht
| Was | Wo (Quelle) | Mechanismus | Ziel | Rotation |
|---|---|---|---|---|
| 7 LXCs (100–106) + VM 200 | PVE-Host | vzdump Snapshot, 03:00 |
/nas/backups/pve |
keep-last=3 |
| Guacamole-DB + Configs | VPS | pg_dump + tar, 02:00 |
/root/backups (VPS, Zwischenstand) |
7 Tage |
| — Pull der VPS-Backups | PVE-Host | rsync über backup_pull_key, 04:00 |
/nas/backups/vps |
folgt VPS-Rotation |
Rebuild-Runbooks
Aufbau
- Übersicht
- PVE-Host
- AdGuard · CT100
- NAS · CT101
- Management-Desktop · CT102
- Vaultwarden · CT103
- VPS · STRATO
- Backup & Restore
- Monitoring · CT104
- Gast-Zugang
Referenz