Backup & Restore¶
Vesana schreibt drei Klassen von Daten, die du sichern musst:
| Klasse | Wo | Wiederherstellbar ohne Backup? |
|---|---|---|
| Datenbank (Postgres) | Compose-Volume pgdata |
Nein |
| Konfiguration | DB + .env (Secrets) |
Bedingt — .env lässt sich mit neuem Setup-Script generieren, aber FIELD_ENCRYPTION_KEY ist unwiederbringlich |
| Wiki-Anhänge / Uploads | Volume uploads |
Nein |
FIELD_ENCRYPTION_KEY ist die einzige Sache, die wirklich nicht ersetzt werden kann
Verschlüsselte DB-Felder (z. B. SNMP-Communities) lassen sich ohne diesen Schlüssel niemals wieder entschlüsseln. Der Schlüssel muss außerhalb des Backup-Volumes liegen.
Automatisches Backup (Sidecar)¶
Vesana enthält einen optionalen Backup-Sidecar im Compose-Stack.
Aktivieren¶
Konfiguration¶
In .env:
| Variable | Default | Bedeutung |
|---|---|---|
BACKUP_SCHEDULE |
0 2 * * * |
Cron-Ausdruck (täglich 02:00 UTC) |
BACKUP_RETENTION_DAYS |
7 |
Älter als X Tage werden gelöscht |
BACKUP_API_TOKEN |
leer | Optional: Admin-Token für zusätzlichen Config-Export |
Nach Änderungen den Sidecar neu starten:
Was wird gesichert¶
- Datenbank-Dump —
pg_dumpals komprimiertes SQL:backup-YYYYMMDD-HHMMSS.sql.gz - Config-Export (wenn
BACKUP_API_TOKENgesetzt) — JSON-Snapshot von Tenants, Hosts, Profilen, Alert-Rules etc. via/api/v1/admin/export - Compose-File-Snapshot — die zur Backup-Zeit aktive
docker-compose.prod.yml
Alle Backups landen im Volume backups, gemountet auf /opt/vesana/backups.
Icon-Assets-Volume separat sichern
Das icon-assets-Volume (/data/icon-assets) wird nicht vom Standard-Sidecar
erfasst — es enthält die hochgeladenen Hersteller-/Profil-/Host-Logos als Binär-
Dateien. Bei einem Restore ohne dieses Volume kennen die DB-Tabellen icon_assets
+ icon_bindings zwar noch jedes Asset, aber die Datei fehlt → der API-Endpoint
/api/v1/icons/{id}/file liefert 404 und das Frontend fällt auf Lucide-Icons
zurück. Empfehlung: in dasselbe Backup-Skript aufnehmen, das auch das nginx-SSL-Volume
sichert. Beispiel:
Inhalt prüfen¶
ls -la /opt/vesana/backups/
docker compose -f /opt/vesana/docker-compose.prod.yml run --rm backup ls -la /backups/
Manuelles Backup¶
Ohne Sidecar:
cd /opt/vesana
docker compose -f docker-compose.prod.yml exec postgres \
pg_dump -U vesana vesana | gzip > backup-$(date +%Y%m%d-%H%M%S).sql.gz
Config-Export per API:
curl -H "Authorization: Bearer <ADMIN_JWT>" \
https://deine-domain.tld/api/v1/admin/export \
> config-export-$(date +%Y%m%d).json
Sicheres Aufbewahren¶
| Inhalt | Wohin | Wer braucht Zugriff |
|---|---|---|
pgdata-Backups |
Externes NAS, S3, Veeam, USB-Disk im Tresor | Backup-Operator |
FIELD_ENCRYPTION_KEY |
Passwort-Manager (Bitwarden, 1Password, KeePass) | Lukas / Operations-Eigentümer |
SECRET_KEY (JWT) |
Passwort-Manager | nur bei Disaster-Recovery |
.env als Ganzes |
Verschlüsselter Container, getrennt vom DB-Backup | Backup-Operator |
Keys nicht ins gleiche Backup wie die Datenbank
Wer die Datenbank UND den FIELD_ENCRYPTION_KEY zusammen besitzt, kann SNMP-Communities und alle anderen verschlüsselten Felder im Klartext lesen. Backup und Schlüssel räumlich/logisch trennen.
Restore¶
Datenbank¶
cd /opt/vesana
# 1. Schreibende Services stoppen
docker compose -f docker-compose.prod.yml stop api receiver worker
# 2. Backup einspielen
gunzip -c backup-YYYYMMDD-HHMMSS.sql.gz | \
docker compose -f docker-compose.prod.yml exec -T postgres \
psql -U vesana vesana
# 3. Services hochfahren
docker compose -f docker-compose.prod.yml up -d
Wenn der Restore in eine frische Instanz geht, vorher das pgdata-Volume leeren:
docker compose -f docker-compose.prod.yml down
docker volume rm vesana_pgdata
docker compose -f docker-compose.prod.yml up -d postgres
# warten bis postgres healthy
gunzip -c backup-YYYYMMDD-HHMMSS.sql.gz | \
docker compose -f docker-compose.prod.yml exec -T postgres \
psql -U vesana vesana
docker compose -f docker-compose.prod.yml up -d
Config-Import¶
curl -X POST \
-H "Authorization: Bearer <ADMIN_JWT>" \
-H "Content-Type: application/json" \
-d @config-export.json \
https://deine-domain.tld/api/v1/admin/import
Achtung: ein vollständiger Config-Import überschreibt bestehende Tenants und Hosts. Nur in eine leere Instanz importieren.
Disaster-Drill¶
Restore ohne regelmäßigen Test ist kein Backup, sondern Hoffnung. Empfohlen: einmal pro Quartal.
# 1. Test-Server / lokale VM mit aktuellem Backup
# 2. Aktuellsten Backup-Tarball + .env (mit FIELD_ENCRYPTION_KEY) bereitstellen
# 3. Setup-Script ausführen, dabei SECRET_KEY und FIELD_ENCRYPTION_KEY aus dem Original-.env übernehmen
# 4. pgdata leeren, gunzip-restore wie oben
# 5. Login mit Original-Admin-Account testen
# 6. Mindestens einen Host öffnen, einen Service mit verschlüsseltem snmp_community ansehen → muss funktionieren
Wenn Schritt 6 fehlschlägt (snmp_community erscheint maskiert obwohl Wert eigentlich da war), ist der FIELD_ENCRYPTION_KEY aus dem Backup nicht der richtige.
Empfohlene Strategie¶
- Backup-Sidecar aktiv für tägliche DB-Dumps
FIELD_ENCRYPTION_KEYextern im Passwort-Manager + Print-Backup im Tresor- Wöchentliche Off-Site-Kopie der Backup-Files (S3, NFS, USB-Rotation)
- Quartals-Drill auf einer separaten Maschine
Anschluss¶
- Updates — der Updater erstellt vor jedem Update automatisch ein Pre-Update-Backup
- Sicherheit → Verschlüsselung — Detail-Erklärung Field-Encryption