homelab-showcase/07-backup-restore.md

5.3 KiB
Raw Blame History

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 4 LXCs

(crontab -l 2>/dev/null; echo "0 3 * * * vzdump 100 101 102 103 --mode snapshot --compress zstd --dumpdir /nas/backups/pve --prune-backups keep-last=3 >> /var/log/vzdump-cron.log 2>&1") | crontab -

LVM-Thin-Snapshot-Modus (--mode snapshot) → alle 4 Container 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.

3. VPS: rsync/cron nachinstallieren

apt update && apt install -y rsync cron
systemctl enable --now cron

⚠️ NICHT vergessen: Die minimale Debian-13-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: nächtlicher Pull-Cron

(crontab -l 2>/dev/null; echo '0 4 * * * rsync -av -e "ssh -i /root/.ssh/backup_pull_key" root@<TS_IP_VPS>: /nas/backups/vps/ >> /var/log/vps-backup-pull.log 2>&1') | crontab -

Zeitlich nach beiden vorherigen Jobs (02:00 VPS-Backup, 03:00 vzdump).

⚠️ 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 4 Instanzen):

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.

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
4 LXCs (100103) 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