# Backup & Restore — Lageplan Automatisches Backup als eigener Container (`backup`). Sichert **täglich**: - **Datenbank** (PostgreSQL) → `db_.dump` (pg_dump, custom/komprimiert) - **Hochgeladene Dateien** (MinIO: Pläne, Logos, Symbole) → `files_.tar.gz` **Aufbewahrung:** 30 Tage (ältere werden automatisch gelöscht) — passt zur Datenschutzerklärung. Einstellbar über `BACKUP_RETENTION_DAYS` und `BACKUP_INTERVAL_HOURS` (siehe `.env.example`). Die Backups liegen im Docker-Volume **`backups_data`** (Pfad im Container: `/backups`). --- ## Einrichtung Der `backup`-Service ist in `docker-compose.yml` und `deploy/portainer-stack.yml` enthalten. Nach dem nächsten Stack-Update (Portainer: Stack neu deployen) läuft er automatisch. Prüfen, dass er läuft und sichert: ``` docker logs lageplan-backup # oder: docker compose logs backup docker compose exec backup ls -lh /backups ``` Beim ersten Start wird sofort ein Backup erstellt, danach alle 24 h. ### Empfehlung: Backups aus dem Container heraus auf den NAS legen (Off-Site) Standardmässig liegen die Backups in einem Docker-Volume. Damit sie ausserhalb von Docker (z. B. auf einer NAS-Freigabe, die du separat sicherst) landen, im Stack den `backup`-Service auf einen **Host-Pfad** umstellen: ```yaml volumes: - /volume1/backups/lageplan:/backups # statt backups_data:/backups - minio_data:/minio:ro ``` So kannst du die Dateien einfach kopieren/auf ein anderes Medium spiegeln. **Wichtig:** Ein Backup auf demselben Gerät wie die Daten schützt nicht vor Geräteausfall/Diebstahl — kopiere die Backups regelmässig an einen zweiten Ort. --- ## Wiederherstellen (Restore) > **Destruktiv.** Vorher die Web-App stoppen (`docker stop lageplan-web`) und sicherstellen, dass > niemand arbeitet. ### 1) Datenbank ``` # Verfügbare Dumps anzeigen docker compose exec backup ls -lh /backups # Wiederherstellen (Dateiname einsetzen) docker compose exec backup pg_restore --clean --if-exists --no-owner \ -h db -U -d /backups/db_2026-07-23_030000.dump ``` ### 2) Dateien (Uploads/MinIO) Da `/minio` im backup-Container schreibgeschützt ist, den Tarball in einen Container mit Schreibzugriff auf das MinIO-Volume entpacken: ``` docker run --rm -v minio_data:/data -v backups_data:/backups alpine \ sh -c "tar xzf /backups/files_2026-07-23_030000.tar.gz -C /data" ``` Danach Web-App wieder starten: `docker start lageplan-web`. --- ## Restore-Test (mindestens 1× durchführen und hier notieren) Ein Backup ist erst dann ein Backup, wenn ein Restore nachweislich funktioniert. Empfehlung: **halbjährlich** auf einer Testumgebung durchspielen. | Datum | Getestet von | Ergebnis | Bemerkung | |-------|--------------|----------|-----------| | | | | | --- ## Was ist NICHT im Backup? - Zahlungsdaten liegen bei Stripe (nicht in der App). - E-Mails liegen beim SMTP-Anbieter. - Secrets (`NEXTAUTH_SECRET`, MinIO-Keys) — separat sicher aufbewahren (Passwort-Manager), sie stehen nicht in den Backups. --- # GUI-Backup (verschlüsselt, extern: SFTP / Nextcloud) Zusätzlich zum lokalen Container-Backup gibt es ein **über die Oberfläche konfigurierbares**, verschlüsseltes Off-Site-Backup. Als **SERVER_ADMIN**: **Administration → Backup**. - **Ziele:** SFTP oder Nextcloud (WebDAV). - **Verschlüsselung:** AES-256-GCM mit einer **Passphrase** (im Passwort-Manager aufbewahren — ohne sie ist kein Restore möglich). Zugangsdaten werden verschlüsselt in der DB gespeichert. - **Zeitplan:** aus / täglich / wöchentlich + Aufbewahrung in Tagen (prunt alte Backups am Ziel). - **Ablauf:** Speichern → **Verbindung testen** → **Jetzt sichern**. Der automatische Lauf braucht `CRON_SECRET` (siehe `.env.example`); der interne Scheduler prüft stündlich die Fälligkeit. Hochgeladen wird eine Datei `lageplan_.tar.gz.enc` (enthält `database.dump` + `files/`). ## Restore eines GUI-Backups ``` # 1) Datei vom Ziel herunterladen, dann entschlüsseln (Passphrase bereithalten): node scripts/decrypt-backup.js lageplan_2026-07-23-03-00-00.tar.gz.enc backup.tar.gz "DEINE-PASSPHRASE" # 2) Entpacken tar xzf backup.tar.gz # -> database.dump + files/ # 3) Datenbank wiederherstellen (destruktiv, App vorher stoppen) docker cp database.dump lageplan-db:/tmp/database.dump docker exec lageplan-db pg_restore --clean --if-exists --no-owner -U -d /tmp/database.dump # 4) Dateien zurück in MinIO (Ordner files/ in den Bucket) docker run --rm -v minio_data:/data -v "$PWD/files":/src alpine sh -c "cp -r /src/* /data// 2>/dev/null || true" ``` Danach App wieder starten. **Restore einmal testen** (Tabelle oben ausfüllen).