Admin → Backup: Ziel (SFTP oder Nextcloud/WebDAV) konfigurieren, Verbindung testen, Jetzt sichern,
Zeitplan (aus/täglich/wöchentlich) + Aufbewahrung. Best Practice: verschlüsselt + off-site.
- Verschlüsselung: Backup als tar.gz (database.dump + MinIO-Dateien) → AES-256-GCM mit Passphrase.
Zugangsdaten + Passphrase verschlüsselt in DB (src/lib/crypto-secret.ts, Schlüssel aus Server-Secret)
- Engine (src/lib/backup): pg_dump + archiver-Stream aus MinIO + Stream-Verschlüsselung + Upload + Prune
- Ziel-Adapter: SFTP (ssh2-sftp-client) + WebDAV (webdav), je test/upload/list/delete
- API (SERVER_ADMIN): /api/admin/backup/{config,test,run}; öffentlich per CRON_SECRET: /api/cron/backup
- Scheduler in server-custom.js (stündliche Fälligkeitsprüfung)
- Dockerfile: postgresql16-client (pg_dump) + runtime-Libs; next.config serverExternalPackages
- scripts/decrypt-backup.js + docs/BACKUP.md (Restore-Anleitung), .env.example (CRON_SECRET, BACKUP_ENC_KEY)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.7 KiB
Backup & Restore — Lageplan
Automatisches Backup als eigener Container (backup). Sichert täglich:
- Datenbank (PostgreSQL) →
db_<zeitstempel>.dump(pg_dump, custom/komprimiert) - Hochgeladene Dateien (MinIO: Pläne, Logos, Symbole) →
files_<zeitstempel>.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:
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 <POSTGRES_USER> -d <POSTGRES_DB> /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_<zeitstempel>.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 <POSTGRES_USER> -d <POSTGRES_DB> /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/<MINIO_BUCKET>/ 2>/dev/null || true"
Danach App wieder starten. Restore einmal testen (Tabelle oben ausfüllen).