Files
archivmail/docs/BACKUP_RESTORE_RUNBOOK.md
T
sysops 4eba165250 docs(PROJ-66): Backup & Restore Runbook schreiben
docs/BACKUP_RESTORE_RUNBOOK.md deckt beide Sicherungsebenen ab: App-eigenes
CLI-Backup (archivmail backup/restore, PROJ-66) und Infrastruktur-Ebene
(PBS + Sync auf Zweitserver, pct restore). Enthaelt Bitwarden-Keyfile-
Escrow-Anleitung, Pflicht-Verifikationsschritte (reconcile/reindex/
Stichprobe), Rotation-vs-DSGVO-Hinweis und den offenen Punkt "noch kein
Restore-Test durchgefuehrt" als groesste verbleibende Luecke.

AC 8 aus PROJ-66 damit erfuellt; AC 6 (Restore-Test) und AC 7 (Alerting)
bleiben offen.
2026-07-05 21:11:39 +02:00

7.7 KiB
Raw Blame History

Backup & Restore Runbook archivmail

Bezug: features/PROJ-66-backup-strategie.md (Spec + Entscheidungsverlauf), docs/GOBD_DSGVO_CHECKLIST.md Punkt 7 (Löschsperre) und 15 (Mandantentrennung). Ziel: belegen können, dass ein Totalausfall von Store, Keyfile oder PostgreSQL nicht zu unwiederbringlichem Mail-Verlust führt — und dass ein Restore im Ernstfall tatsächlich funktioniert, nicht nur theoretisch existiert.

Gilt für: archivmail backup/archivmail restore (PROJ-66, App-eigenes CLI-Backup) und ergänzend die Proxmox Backup Server (PBS) + Sync-auf- Zweitserver-Sicherung auf Infrastruktur-Ebene.

Grundsatz

Store, Keyfile und PostgreSQL-Metadaten sind nur gemeinsam nutzbar:

  • Store ohne DB: Dateien vorhanden, aber keine Zuordnung zu Mail/Tenant.
  • Keyfile ohne Store: irrelevant, nichts zu entschlüsseln.
  • DB ohne Keyfile: Metadaten da, Mail-Inhalte nicht mehr lesbar (GoBD-Verstoß).

Ein Backup, das nur eine dieser drei Komponenten sichert, ist wertlos. Ein Backup, das nie zurückgespielt wurde, ist nicht verifiziert — siehe "Regelmäßiger Restore-Test" unten.

Zwei unabhängige Sicherungsebenen

  1. Infrastruktur-Ebene (PBS + Sync auf Zweitserver) — läuft vom Proxmox-Host aus, sichert den kompletten Container (Store+Keyfile+ Config+PostgreSQL-Datendir+Audit-Log in einem Snapshot). Von innerhalb des Containers nicht sichtbar/steuerbar. Siehe Memory project_backup_infra_pbs für CLI-Befehle (vzdump, pct restore).
  2. App-eigenes Backup (archivmail backup/restore) — ergänzt Ebene 1 um einen anwendungskonsistenten pg_dump (statt reinem Crash-Consistency- Snapshot) und einen selektiven, von PBS unabhängigen Restore-Weg.

Keine der beiden Ebenen ersetzt die andere. Ein vollständiger Restore-Test sollte mindestens einmal beide Wege durchlaufen haben (siehe unten).

Backup erstellen

archivmail backup --config /etc/archivmail/config.yml --dest <ziel> --keep 14
  • <ziel> MUSS auf einem vom Produktivsystem physisch getrennten Ziel liegen (zweiter Host, NAS, o.ä.) — siehe PROJ-66-Spec, Abschnitt "Backup-Ziel: On-Site vs. Off-Site". Ein Backup auf derselben Platte schützt nicht vor Hardware-Ausfall.
  • Schreibt <ziel>/<timestamp>/: postgres.dump (pg_dump -Fc), store/ (Hardlink-erhaltende Kopie, PROJ-65-Tenant-Ordner bleiben ohne Speicherplatz-Verdopplung erhalten), keyfile, config.yml, audit.log (best-effort).
  • --keep N behält die letzten N Backup-Verzeichnisse, löscht ältere — nur nach erfolgreichem Lauf. Ein fehlgeschlagener Lauf lässt den partiellen Ordner stehen und rotiert nichts weg (Diagnose-Möglichkeit, kein Verlust eines guten alten Backups).
  • Cron-Aktivierung: deploy/cron.d/archivmail enthält eine auskommentierte Beispielzeile. Erst aktivieren, wenn ein konkretes --dest-Ziel feststeht.

Keyfile-Escrow (Bitwarden)

Zusätzlich zum Backup: das Keyfile als unabhängige Kopie in Bitwarden hinterlegen (Secure Note, eigene Collection mit eingeschränktem Zugriff — das Keyfile entschlüsselt das gesamte Archiv aller Mandanten).

cat /etc/archivmail/keyfile
# → Wert 1:1 in eine neue Bitwarden Secure Note kopieren, Metadaten
#   ergänzen (Server/Installation, Datum, wer hinterlegt hat).

Zweck: unabhängiger Wiederherstellungsweg, falls PBS/Snapshot-Infrastruktur selbst nicht verfügbar ist. Ersetzt nicht das App-Backup — beide parallel pflegen. Bei künftiger Keyfile-Rotation (aktuell nicht vorgesehen) den Bitwarden-Eintrag im selben Schritt aktualisieren.

Restore durchführen

Vorbereitung:

  1. Zielsystem: frischer Server/Container oder vorhandenes System im Wartungsmodus. archivmail/archivmail-web müssen gestoppt sein — archivmail restore stoppt/startet die Dienste NICHT selbst.
  2. Backup-Verzeichnis identifizieren: <ziel>/<timestamp>/.

Durchführung:

systemctl stop archivmail archivmail-web

archivmail restore --config /etc/archivmail/config.yml \
  --source <ziel>/<timestamp> [--force]
  • Ohne --force: bricht ab, wenn store_path/Keyfile bereits Inhalt haben — Schutz gegen versehentliches Überschreiben eines laufenden Systems. --force nur auf einem bewusst freigeräumten/frischen Zielsystem nutzen.
  • --skip-db: überspringt pg_restore, falls die Datenbank bereits separat wiederhergestellt wurde (z.B. via PBS-Snapshot der DB-Ebene).
  • Stellt Store (Hardlinks erhalten), Keyfile, Config und PostgreSQL (pg_restore --clean --if-exists) wieder her. Gibt am Ende die Pflicht-Verifikationsschritte aus (siehe unten).

Verifikation (Pflicht, nicht optional):

systemctl start archivmail archivmail-web
# Health-Check wie im Standard-Deploy-Workflow (Backend ✓, Frontend ✓)

archivmail reconcile --days 7           # PROJ-52: Zählvergleich Store vs. DB muss aufgehen
archivmail reindex                      # Manticore aus Store+DB neu aufbauen
# Stichprobe: 3-5 zufällige Mails über die UI öffnen und lesbar entschlüsseln
# lassen — beweist, dass Keyfile+Store+DB zueinander passen.

Restore-Ergebnis (Mail-Zahl, Zeitstempel, Dauer, welche Verifikationsschritte liefen) dokumentieren — Prinzip "Zählen, Prüfen, Belegen" wie im Migrations-Runbook.

Restore aus einem PBS-Snapshot (Infrastruktur-Ebene)

Auf dem Proxmox-Host (nicht im Container):

pct restore <neue-id> <pbs-storage>:backup/ct/<quell-id>/<timestamp> --storage <ziel-storage>

Restored als neuer Container mit <neue-id> — der Quell-Container bleibt unangetastet. Danach dieselbe Verifikation wie oben (reconcile, reindex, Stichprobe) im neuen Container durchführen. PostgreSQL kam dabei aus einem Crash-Consistency-Snapshot, nicht aus einem pg_dump — WAL-Replay beim Start ist der Normalfall, sollte aber beim Test explizit beobachtet werden (journalctl -u postgresql auf Recovery-Meldungen prüfen).

Regelmäßiger Restore-Test

Ein Backup, das nie zurückgespielt wurde, ist nicht verifiziert. Empfehlung: mindestens vierteljährlich einen vollständigen Restore auf einen isolierten Test-Container durchführen (nicht auf 131/132!) — einmal über archivmail restore (App-Backup) und mindestens einmal über pct restore (PBS-Snapshot), jeweils mit vollständiger Verifikation. Bislang (Stand 2026-07-05) wurde noch kein Restore-Test durchgeführt — das ist die kritischste offene Lücke in der Backup-Strategie, nicht das Fehlen eines Backup-Ziels.

Rotation vs. DSGVO-Löschung

Backup-Rotation (--keep N, bzw. PBS-Retention) ist unabhängig von der GoBD-Aufbewahrungsfrist der Mails selbst (PROJ-51). Eine in Produktion bereits DSGVO-gelöschte Mail (PROJ-50) kann in einem älteren Backup noch enthalten sein. Rotation muss kurz genug sein, dass gelöschte Daten nach spätestens N Zyklen auch aus Backups verschwinden — Zeitraum mit Datenschutzbeauftragtem/Nutzer abstimmen, kein rein technisches Thema.

Monitoring / Alerting (noch offen)

Größtes Risiko bei Cron-Backups: der Job schlägt still fehl, niemand merkt es bis zum Ernstfall (Lehre aus PROJ-58: Cron-Zeilen fehlten wochenlang trotz aktivem Code). Aktuell nicht gebaut: ein sichtbarer Alarm, wenn das letzte erfolgreiche Backup älter als ein Schwellenwert ist. Bis dahin: Log-Datei des Backup-Cron-Jobs (/var/log/archivmail/backup.log, sobald der Cron aktiviert ist) manuell im Rahmen der regulären Betriebs-Checks prüfen.

Verweis

  • features/PROJ-66-backup-strategie.md — vollständige Spec, Optionsabwägung, QA-Ergebnisse.
  • docs/MIGRATION_RUNBOOK.md — Vorbild für den "Zählen, Prüfen, Belegen"-Stil.
  • Memory project_backup_infra_pbs — PBS/Sync-Infrastruktur-Details, vzdump/pct restore-Befehle.
  • Memory project_132_teilproduktiv — 132 ist kein reiner Testserver, Backup-Sorgfalt gilt dort genauso wie auf 131.