Zum Inhalt springen
Q
QuoteNode

Wiki

Backup und Wiederherstellung

Wie QuoteNode Datenbank- und Datei-Backups handhabt — age-verschlüsselte Archive, manifestgesteuerte Wiederherstellung, Recovery-Kits und Runtime-Profile.

Backup und Wiederherstellung

QuoteNode enthält ein integriertes Backup-System, das Ihre Geschäftsdaten vor Hardwareausfällen, versehentlichem Löschen und betrieblichen Vorfällen schützt.

Was gesichert wird

Jedes Backup erstellt ein Archiv mit:

  1. Datenbank-Dump — ein vollständiger PostgreSQL-Dump im Custom-Format (pg_dump --format=custom), einschließlich aller Tabellen, Sequenzen und Constraints.
  2. Dateispeicher — alle hochgeladenen Dateien (Produktbilder, Firmenlogos) und generierten PDFs, archiviert als files.tar.gz mit relativen Pfaden.
  3. Backup-Manifest — eine Datei backup-manifest.json mit Quellversion, Verschlüsselungsmodus, Datenbank-Payload-Modus, Prüfsummen und Metadaten zur Wiederherstellungskompatibilität.
  4. Integritäts-Prüfsummen — eine Datei checksums.sha256 mit SHA-256-Hashes aller Archivkomponenten.

Die drei Verschlüsselungsebenen

Ein Backup umfasst drei unabhängige Geheimnisse/Einstellungen. „Verschlüsseltes Backup” ist mehrdeutig — benennen Sie immer die Ebene:

  1. Privates AGE-Identity — entsperrt nur das äußere .tar.age-Archiv. Es stammt aus dem Recovery Kit und wird beim Wiederherstellen/Verifizieren verwendet (RESTORE_AGE_IDENTITY_FILE). Es enthält oder ersetzt nicht den DB_ENCRYPTION_KEY.
  2. DB_ENCRYPTION_KEY — entschlüsselt die APP_ENCRYPTED-Datenbankfelder und die betrieblichen Geheimnisse (MFA, SMTP, Offer-E-Mail-Payloads). Sichern Sie ihn getrennt vom Archiv und übergeben Sie ihn nie als Kommandozeilenargument.
  3. ENCRYPT_PII — die Speicherrichtlinie des Ziel-Laufzeitsystems. Es entschlüsselt kein Archiv; seine Änderung ersetzt nie den korrekten Schlüssel oder die Restore-Nachverarbeitung.

Bewahren Sie Ihre .env- / Deployment-Geheimnisse und das Recovery Kit an getrennten geschützten Orten auf.

Archivverschlüsselung

Neue verschlüsselte Backups verwenden die age-Empfängerverschlüsselung (AGE_RECIPIENT). Dies ist das einzige unterstützte Verschlüsselungsmodell für neue Archive.

  • Der Backup-Prozess verwendet ausschließlich einen öffentlichen age-Empfänger (age1...) — auf dem Server wird kein privater Schlüssel zum Erstellen von Backups benötigt.
  • Verschlüsselte Archive haben die Endung .tar.age.
  • Die SHA-256-Prüfsumme des verschlüsselten Archivs wird neben dem Archiv abgelegt, damit der Betreiber sie verifizieren kann.
  • Die Entschlüsselung erfordert die passende private age-Identität, die ausschließlich im Recovery-Kit des Betreibers aufbewahrt wird.
  • Die rohe private age-Identität wird nach der Einrichtungsbestätigung niemals in der Datenbank, in Logs, in DTOs oder in Prozessargumenten gespeichert.

Unverschlüsselte lokale Backups

Lokale unverschlüsselte Backups (.tar) sind nur zulässig, wenn alle folgenden Bedingungen erfüllt sind:

  • BACKUP_ARCHIVE_ENCRYPTION_MODE=NONE
  • BACKUP_ALLOW_UNENCRYPTED_LOCAL_BACKUP=true
  • Das Ziel ist lokaler Speicher
  • BACKUP_DATABASE_PAYLOAD_MODE=APP_ENCRYPTED
  • Der Administrator hat das Risiko in der Oberfläche ausdrücklich bestätigt

Unverschlüsselte Archive werden für Remote-Speicher und für PII-entschlüsselte Payloads blockiert.

Konfiguration (.env). Der age-Empfänger wird vom Recovery-Kit (Admin → Sicherheit → Backup) generiert und in der Instanz gespeichert — Sie hinterlegen keinen Schlüssel in der .env. Setzen Sie nur die beiden Modus-Variablen:

BACKUP_ENABLED=true
# Second, dedicated switch — default off. Set true only on the backup-worker to run scheduled backups.
BACKUP_SCHEDULER_ENABLED=true
BACKUP_ARCHIVE_ENCRYPTION_MODE=AGE_RECIPIENT
BACKUP_DATABASE_PAYLOAD_MODE=APP_ENCRYPTED
# Alternative — encrypted archive + decrypted PII (cross-instance portability):
# BACKUP_DATABASE_PAYLOAD_MODE=PII_DECRYPTED
BACKUP_ALLOW_UNENCRYPTED_LOCAL_BACKUP=false

So deaktivieren Sie die Archivverschlüsselung (nur lokal, nicht empfohlen — PII bleibt im Payload weiterhin verschlüsselt):

BACKUP_ARCHIVE_ENCRYPTION_MODE=NONE
BACKUP_ALLOW_UNENCRYPTED_LOCAL_BACKUP=true

Bei AGE_RECIPIENT schlägt das Backup laut fehl, wenn noch kein Empfänger generiert wurde, anstatt ein ungeschütztes Archiv zu schreiben. Die Kombination NONE + PII_DECRYPTED wird abgelehnt.

Datenbank-Payload-Modi

Modus Beschreibung
APP_ENCRYPTED Standard. PII-Felder bleiben mit dem Anwendungsschlüssel DB_ENCRYPTION_KEY verschlüsselt. Wiederherstellung auf denselben Schlüssel (passender Fingerabdruck) erfordert keinen zusätzlichen Schritt; Wiederherstellung auf einen anderen Schlüssel erfordert den ursprünglichen Quellschlüssel (über PII_REKEY_OLD_KEY) für einen Offline-Rekey, sonst wird die Wiederherstellung vor jeder Änderung blockiert.
PII_DECRYPTED PII-Felder werden vor der Archivierung in eine temporäre Datenbank entschlüsselt. Passwort-Hashes der Benutzer bleiben unverändert. Erfordert die Archivverschlüsselung AGE_RECIPIENT. Nützlich für die Portabilität zwischen Instanzen.

Beim Wiederherstellen eines PII_DECRYPTED-Backups in ein Ziel mit ENCRYPT_PII=true verschlüsselt der Wiederherstellungsprozess die PII automatisch mit dem DB_ENCRYPTION_KEY des Ziels neu, bevor die Anwendung normal startet.

Recovery-Kit

Während der Einrichtung generiert (oder importiert) der Administrator einen age-Empfänger für verschlüsselte Backups. Das Recovery-Kit ist eine ZIP-Datei mit:

  • quotenode-age-identity.txt — die private age-Identität (wird nur einmal zurückgegeben)
  • quotenode-age-recipient.txt — der öffentliche Empfänger und der Fingerabdruck
  • quotenode-backup-verify.sh — lokales Verifizierungsskript (Linux/macOS)
  • quotenode-backup-verify.ps1 — lokales Verifizierungsskript (Windows)
  • README.md — Befehle für Verifizierung und Wiederherstellung

Kritisch: Die private age-Identität wird nur einmal bei der Kit-Erstellung zurückgegeben. Geht sie verloren, können verschlüsselte Archive nicht mehr entschlüsselt werden. Bewahren Sie das Recovery-Kit an einem sicheren Offline-Ort auf.

Der Administrator muss bestätigen, dass das Kit gespeichert und getestet wurde, bevor der Einrichtungsassistent das Backup als produktionsbereit markiert.

Lokale Backup-Verifizierung

Heruntergeladene Backups können außerhalb der laufenden Anwendung mit den Skripten des Recovery-Kits verifiziert werden:

scripts/quotenode-backup-verify.sh \
  --archive /path/backup.tar.age \
  --identity /path/quotenode-age-identity.txt \
  --expected-sha256 <sha256-from-ui-or-sidecar>

Das Verifizierungstool:

  1. Prüft die SHA-256-Prüfsumme des verschlüsselten Archivs gegen den erwarteten Wert oder die .sha256-Begleitdatei.
  2. Entschlüsselt mit age -d in ein privates temporäres Verzeichnis.
  3. Validiert die tar-Einträge (lehnt absolute Pfade, .., Symlinks und Gerätedateien ab).
  4. Prüft, ob backup-manifest.json existiert und eine unterstützte Version hat.
  5. Führt sha256sum -c checksums.sha256 aus.
  6. Führt pg_restore --list db.dump aus, sofern pg_restore verfügbar ist.
  7. Entfernt standardmäßig die entschlüsselten temporären Daten (mit --keep-decrypted werden sie behalten).

Online-Backup (Standard)

Online-Backups laufen, während die Anwendung Anfragen bedient. Es gibt keine Ausfallzeit.

  • pg_dump im Custom-Format erstellt einen transaktional konsistenten Snapshot, ohne Tabellen zu sperren oder Abfragen zu blockieren.
  • Der Dateispeicher wird parallel archiviert — hochgeladene Dateien und PDFs sind nach der Erstellung unveränderlich, sodass das Archiv stets konsistent ist.
  • PostgreSQL muss für einen konsistenten Datenbank-Dump nicht angehalten werden.

Wie Backups laufen: Auswahl einer Topologie

Es gibt zwei Möglichkeiten, geplante Backups kontinuierlich auszuführen. Beide erzeugen identische Archive — sie unterscheiden sich nur darin, wo der Backup-Job ausgeführt wird.

Topologie Wann verwenden Container
Dedizierter Worker (Standard) Produktion. Backups laufen in einem separaten backup-worker-Container, sodass sie nie mit dem Web-Traffic um Speicher oder CPU konkurrieren. Web-Backend + backup-worker
In-Process Eigene Setups mit nur einem Backend und eigener Compose-Datei (die Release-Pakete verwenden es nicht). Das Web-Backend führt die Backups selbst aus — ein Container weniger im Betrieb. nur Web-Backend

Der Backup-Job selbst ist in beiden Fällen dieselbe Shell-Pipeline (pg_dump → Archivierung → Verschlüsselung), die außerhalb des JVM-Heaps läuft, sodass ihr Speicherbedarf gering ist.

So wählen Sie eine Topologie

Zwei Umgebungsvariablen arbeiten zusammen:

  • JOBS_MODE entscheidet, welcher Container den geplanten Backup-Job tatsächlich ausführt.
  • BACKUP_RUNTIME_PROFILE ist ein Label, das in Backup-Logs und -Manifesten festgehalten wird, damit Sie im Nachhinein nachvollziehen können, wie jedes Backup erstellt wurde.
Topologie Web-Backend Backup-Worker
Dedizierter Worker (Standard) JOBS_MODE=web + BACKUP_RUNTIME_PROFILE=WORKER Container mit JOBS_MODE=backup-only
In-Process JOBS_MODE=all + BACKUP_RUNTIME_PROFILE=WEB_MAINTENANCE (keiner)

Die Release-Pakete verwenden für jede Installation (Server, LAN, Laptop, NAS und Coolify) die Topologie dedizierter Worker: Beide Compose-Dateien enthalten einen Dienst backup-worker und setzen JOBS_MODE pro Dienst. Im Standalone-Paket hat diese Compose-Einstellung Vorrang vor .env, daher erfordert der In-Process-Modus (JOBS_MODE=all + BACKUP_RUNTIME_PROFILE=WEB_MAINTENANCE) eine selbst gepflegte Compose-Datei.

Referenz der Runtime-Profile

BACKUP_RUNTIME_PROFILE akzeptiert vier Werte. Die ersten beiden gelten für die obigen kontinuierlichen Topologien; die letzten beiden für einmalige, vom Betreiber gesteuerte Backups.

Profil Beschreibung
WORKER Standard. Ein permanenter backup-worker-Container führt geplante Backups per Cron aus.
WEB_MAINTENANCE Das Web-Backend führt geplante Backups direkt aus. Es ist kein zusätzlicher Worker-Container erforderlich.
ONE_SHOT Ein externer Cron oder Betreiber führt operator/backup-one-shot.sh aus und beendet sich. Kein permanenter Worker.
OFFLINE_MAINTENANCE Ein automatisiertes Skript kündigt die Ausfallzeit an, stoppt Frontend/Backend/backup-worker, lässt PostgreSQL laufen, führt das Backup aus, startet die Dienste neu und prüft den Zustand.

Einmaliges Backup (one-shot)

COMPOSE_PROJECT_NAME=quotenode COMPOSE_FILE=$PWD/docker-compose.yml COMPOSE_ENV_FILE=$PWD/.env \
  BACKUP_ARCHIVE_ENCRYPTION_MODE=AGE_RECIPIENT \
  BACKUP_AGE_RECIPIENT=age1... \
  ./operator/backup-one-shot.sh

Offline-Wartung

COMPOSE_PROJECT_NAME=quotenode COMPOSE_FILE=$PWD/docker-compose.yml COMPOSE_ENV_FILE=$PWD/.env \
  BACKUP_OFFLINE_NOTICE_SECONDS=300 \
  ./operator/backup-offline-maintenance.sh

Das Offline-Wartungsskript:

  1. Kündigt eine geplante Wartung an (konfigurierbare Karenzzeit).
  2. Aktiviert optional eine statische Wartungsseite, während die Dienste ausfallen.
  3. Stoppt Frontend, Backend und backup-worker (lässt PostgreSQL laufen).
  4. Führt ein einmaliges Backup aus.
  5. Startet alle Dienste neu.
  6. Prüft den Zustand des Backends.
  7. Schreibt ein JSONL-Wartungsjournal auf Host-Ebene.

Schlägt das Skript an irgendeiner Stelle fehl, startet ein Sicherheits-Trap die Dienste automatisch neu.

Verwenden Sie --dry-run, um den Plan ohne Ausführung anzuzeigen:

./operator/backup-offline-maintenance.sh --dry-run

Zeitplanung

  • Standardzeitplan: täglich um 2:00 Uhr (0 0 2 * * *, konfigurierbar über BACKUP_CRON)
  • Aktivierung (zwei Schalter): Geplante Backups laufen nur, wenn sowohl BACKUP_ENABLED=true als auch BACKUP_SCHEDULER_ENABLED=true gesetzt sind. BACKUP_SCHEDULER_ENABLED ist ein dedizierter Kill-Switch, der standardmäßig aus ist, sodass das Aktivieren von Backups nie einen überraschenden ersten geplanten Lauf auslöst. Setzen Sie ihn nur auf dem backup-worker (Laufzeitprofil WORKER/WEB_MAINTENANCE), nie auf dem Web-Tier.
  • Manuelle Auslösung: Administratoren können ein sofortiges Backup über das Admin-Panel oder die API auslösen (POST /api/v1/admin/backup/trigger). Der Auslöser antwortet sofort (HTTP 202), während das Backup asynchron läuft.
  • Parallelitätsschutz: Ein datenbankgestützter Lock stellt sicher, dass über alle Worker und manuellen Auslösungen hinweg immer nur ein Backup gleichzeitig läuft.

Für die Profile ONE_SHOT und OFFLINE_MAINTENANCE ist der interne Scheduler deaktiviert — Backups werden extern verwaltet.

Speicheroptionen

Lokaler Speicher (Standard)

Backups werden in BACKUP_LOCAL_DIR gespeichert (Standard: /app/data/backups), das auf ein Docker-Volume gemappt ist.

In der Produktion sollten lokale Backups durch Remote-Kopien ergänzt werden.

Remote-Speicher über rclone

Wenn BACKUP_RCLONE_REMOTE konfiguriert ist, werden Backups nach der lokalen Erstellung automatisch an ein Remote-Ziel hochgeladen. rclone unterstützt über 70 Cloud-Speicheranbieter, darunter S3-kompatible, Google Cloud Storage, Azure Blob, SFTP und WebDAV.

Backups herunterladen

Das Herunterladen von Backup-Archiven erfordert eine Step-up-Autorisierung:

  1. Navigieren Sie zu Einstellungen > Backup.
  2. Klicken Sie beim Backup-Eintrag auf Herunterladen.
  3. Geben Sie Ihr aktuelles Passwort ein (und den TOTP-Code, falls MFA aktiviert ist).
  4. Das System stellt eine kurzlebige, einmalige Download-Berechtigung aus (Standard: 120 Sekunden).
  5. Das Archiv wird automatisch heruntergeladen.

Das Berechtigungstoken wird nur als SHA-256-Hash gespeichert. Die Erstellung der Berechtigung und der Download werden auditiert und andere Administratoren werden benachrichtigt.

Hinweis: Ein direkter Download ohne Berechtigung gibt 403 zurück. Backups auf Remote-Speicher können nicht über das Admin-Panel heruntergeladen werden.

Metadaten & Audit-Trail

Jedes Backup wird in der Tabelle backup_logs erfasst, mit:

  • Status (RUNNING, SUCCESS, FAILED)
  • Initiiert von (SCHEDULER oder ADMIN)
  • Start-/Abschlusszeit
  • Größe (Bytes) und SHA-256-Prüfsumme
  • Ziel (lokaler Pfad oder Remote-URL)
  • Manifest-Version und Quell-App-Version
  • Archivverschlüsselungsmodus und Fingerabdruck
  • Datenbank-Payload-Modus
  • Runtime-Profil
  • Verifizierungsstatus (NOT_RUN, PASSED, FAILED, SKIPPED_TIMEOUT)
  • Wiederherstellungstest-Status und Wiederherstellungskompatibilitäts-Status

Wiederherstellung

Verwendung von operator/restore.sh

Das kanonische Wiederherstellungsskript deckt den gesamten Lebenszyklus ab:

COMPOSE_PROJECT_NAME=quotenode COMPOSE_FILE_PATH=$PWD/docker-compose.yml COMPOSE_ENV_FILE=$PWD/.env \
  DB_USERNAME="$(grep '^DB_USERNAME=' .env | cut -d= -f2-)" \
  DB_NAME="$(grep '^DB_NAME=' .env | cut -d= -f2-)" \
  DB_PASSWORD="$(grep '^DB_PASSWORD=' .env | cut -d= -f2-)" \
  RESTORE_JOURNAL_DIR=$PWD/restore-journal \
  RESTORE_AGE_IDENTITY_FILE=/path/to/quotenode-age-identity.txt \
  [email protected] \
  RESTORE_ADMIN_PASSWORD_FILE=/path/to/password \
  ./operator/restore.sh /path/to/backup.tar.age

Das Skript:

  1. Extrahiert und validiert das Archiv (Manifest, Prüfsummen, Sicherheit der tar-Einträge).
  2. Führt Preflight-Prüfungen durch (Festplattenkapazität, PostgreSQL-Version, DB-Konnektivität).
  3. Erstellt ein Sicherheitsbackup des Ziels vor jeder destruktiven Operation.
  4. Schreibt ein Wiederherstellungsjournal auf Host-Ebene (JSONL, übersteht den DB-Austausch).
  5. Stellt die Datenbank wieder her.
  6. Bereitet Dateien in einem temporären Verzeichnis auf und tauscht sie an Ort und Stelle aus.
  7. Bewahrt die Zielidentität (Instanz-ID, Backup-Verschlüsselungsmetadaten, Lizenzstatus).
  8. Löscht Sessions, öffentliche Angebotslinks und Benachrichtigungstoken.
  9. Setzt den Admin-Zugang zurück oder erzwingt seine Änderung.
  10. Verschlüsselt PII neu, wenn das Backup PII_DECRYPTED ist und das Ziel ENCRYPT_PII=true hat.

Instanzübergreifende Wiederherstellung

Beim Wiederherstellen eines Backups aus einer anderen Installation ist der Standardmodus PRESERVE_TARGET_IDENTITY:

  • Ziel-Instanz-ID, age-Backup-Empfänger, Recovery-Kit-Metadaten und Lizenzstatus bleiben erhalten.
  • Sessions, öffentliche Angebotslinks und Benachrichtigungstoken werden gelöscht.
  • Das Admin-Passwort wird zurückgesetzt/dessen Änderung erzwungen.
  • Geschäftsdaten werden vollständig ersetzt (kein Merge).

Wiederherstellungskompatibilität

Der Wiederherstellungs-Preflight validiert:

  • Älteres Backup in neuere App: erlaubt (kompatibel).
  • Neueres Backup in ältere App: blockiert.
  • Fehlendes Manifest: blockiert.
  • Falscher Verschlüsselungsschlüssel-Fingerabdruck für APP_ENCRYPTED-Payload: blockiert vor destruktiven Arbeiten.
  • PII_DECRYPTED ohne Archivverschlüsselung: blockiert.

Wiederherstellungsentscheidungen und sichere Aktionen

Restore-Richtlinienentscheidungen stammen aus einer Engine und liefern stabile Reason-Codes; ein blockiertes Ergebnis verändert nichts.

  • APP_ENCRYPTED auf einen anderen Schlüssel, ohne Quellschlüssel → SOURCE_DB_ENCRYPTION_KEY_REQUIRED. Geben Sie den ursprünglichen Schlüssel über PII_REKEY_OLD_KEY an (Umgebungsvariable, nie in argv), um einen Rekey durchzuführen.
  • Falscher Quellschlüssel → SOURCE_DB_ENCRYPTION_KEY_MISMATCH. Geben Sie den korrekten Originalschlüssel an; erzeugen Sie keinen Ersatz.
  • Klartext-PII-Ziel, nicht bestätigt → PLAINTEXT_PII_TARGET_NOT_ACKNOWLEDGED. Setzen Sie RESTORE_ACKNOWLEDGE_PLAINTEXT_PII=true nur, wenn Klartext-PII im Ruhezustand beabsichtigt ist.
  • Kein verifizierter Quellschlüssel (nur PII_DECRYPTED) → betriebliche Geheimnisse können nur ZURÜCKGESETZT werden. Setzen Sie RESTORE_ACKNOWLEDGE_OPERATIONAL_SECRET_RESET=true, um dies zu akzeptieren.
  • Falsche AGE-Identity / Integritätsfehler → das Archiv wird nicht geöffnet und nichts wird verändert.

Folgen des Zurücksetzens betrieblicher Geheimnisse (bei Annahme): MFA-Registrierungen werden zurückgesetzt und Nutzer müssen sich neu einrichten; SMTP wird geleert und ausgehende E-Mails bleiben deaktiviert bis zur Neukonfiguration; nicht wiederherstellbare Offer-E-Mail-Payloads werden bereinigt und können nicht erneut gesendet werden. FEATURE_2FA_ENABLED=false ist nur ein temporäres Notfall-Break-Glass, niemals eine normale Einstellung.

Semantik des Restore-Ergebnisses:

  • Erfolg — DB + Dateien wiederhergestellt, Krypto-Postprocess erledigt, erst dann darf die App Traffic bedienen.
  • Ablehnung vor Mutation — eine blockierte Richtlinie oder ein fehlgeschlagener Preflight lässt das Ziel unverändert.
  • Erfolgreiches Rollback — ein Fehler nach der Mutation rollt DB + Dateien gemeinsam aus dem Ziel-Sicherheitsbackup zurück.
  • Fehlgeschlagenes Rollback — die Anwendung bleibt bewusst GESTOPPT, alle Recovery-Artefakte bleiben erhalten; stellen Sie aus dem Sicherheitsarchiv wieder her.

Nicht tun: einen zufälligen Ersatz-DB_ENCRYPTION_KEY erzeugen; ein Geheimnis als Kommandozeilenargument übergeben; oder die Web-Anwendung starten, bevor der Krypto-Postprocess abgeschlossen ist.

Garantie für Admin-Zugang

Die Wiederherstellung garantiert den Admin-Zugang über eine der folgenden Optionen:

  • RESTORE_ADMIN_PASSWORD_FILE — vom Betreiber bereitgestelltes Passwort (gesetzt mit forcePasswordChange=true)
  • RESTORE_ADMIN_PASSWORD — nur für lokale Verwendung/Tests
  • Automatisch generiertes Einmal-Reset-Token — wird auf der Konsole und im Journal auf Host-Ebene ausgegeben

Probelauf (Dry Run)

RESTORE_DRY_RUN=true ./operator/restore.sh /path/to/backup.tar.age

Validiert das Archiv, prüft Prüfsummen und Manifest und meldet den Wiederherstellungsplan, ohne etwas zu verändern.

Disaster Recovery — Wiederaufbau von Grund auf

Ihr Server ist weg. Sie haben ein Backup-Archiv (.tar.age oder .tar) und einen neuen Server mit Docker. So stellen Sie wieder her.

Schritt 1 — Den neuen Server vorbereiten

mkdir quotenode && cd quotenode

Richten Sie docker-compose.yml und .env wie im Installationshandbuch beschrieben ein.

Kritisch für APP_ENCRYPTED-Backups: Am einfachsten ist die Wiederherstellung mit demselben DB_ENCRYPTION_KEY. Die Wiederherstellung auf einen anderen Schlüssel funktioniert weiterhin, wenn Sie den ursprünglichen Schlüssel über PII_REKEY_OLD_KEY für einen Rekey bereitstellen. Ohne den ursprünglichen Schlüssel sind APP_ENCRYPTED-PII nicht wiederherstellbar (nur ein PII_DECRYPTED-Archiv, dessen betriebliche Geheimnisse dann zurückgesetzt werden müssen) — erzeugen Sie niemals einen zufälligen Ersatzschlüssel.

Kritisch für verschlüsselte Archive: Sie benötigen die private Identitätsdatei des Recovery-Kits (quotenode-age-identity.txt), um das Archiv zu entschlüsseln.

Schritt 2 — Backup und Wiederherstellungsskript kopieren

scp backup.tar.age user@new-server:~/quotenode/
# operator/restore.sh comes with the release package (the release that created the backup, or a later one)
curl -fsSLO https://quotenode.dev/releases/v0.9.8-alpha/quotenode-deploy-v0.9.8-alpha.tar.gz
tar -xzf quotenode-deploy-v0.9.8-alpha.tar.gz --strip-components=1

Schritt 3 — Die Wiederherstellung ausführen

# Only a successful restore starts the remaining services (the restore itself starts PostgreSQL, the backend and
# the frontend). On failure nothing else starts and the restore's exit status is kept: read its message.
# Default installations keep PII unencrypted at rest (ENCRYPT_PII=false); a restore into such a target requires the
# explicit RESTORE_ACKNOWLEDGE_PLAINTEXT_PII acknowledgement below, for this command only. A target with
# ENCRYPT_PII=true in .env does not use it.
export COMPOSE_PROJECT_NAME=quotenode
COMPOSE_FILE_PATH=$PWD/docker-compose.yml COMPOSE_ENV_FILE=$PWD/.env \
  DB_USERNAME="$(grep '^DB_USERNAME=' .env | cut -d= -f2-)" \
  DB_NAME="$(grep '^DB_NAME=' .env | cut -d= -f2-)" \
  DB_PASSWORD="$(grep '^DB_PASSWORD=' .env | cut -d= -f2-)" \
  RESTORE_JOURNAL_DIR=$PWD/restore-journal \
  RESTORE_AGE_IDENTITY_FILE=./quotenode-age-identity.txt \
  [email protected] \
  RESTORE_ADMIN_PASSWORD_FILE=./admin-password \
  RESTORE_ACKNOWLEDGE_PLAINTEXT_PII=true \
  ./operator/restore.sh --fresh-install backup.tar.age \
  && docker compose up -d

Meldet die Wiederherstellung einen Fehler, starten Sie QuoteNode nicht von Hand: Es würde eine leere oder unvollständig wiederhergestellte Installation starten. Lesen Sie die Meldung, beheben Sie die Ursache (zum Beispiel die Identitätsdatei oder DB_ENCRYPTION_KEY) und führen Sie den Block erneut aus. Meldet die Wiederherstellung, dass sie die Anwendung gestoppt und die Wiederherstellungsartefakte aufbewahrt hat, folgen Sie Wiederherstellungsentscheidungen und sichere Aktionen oben.

Schritt 4 — Verifizieren

Öffnen Sie Ihre Domain und melden Sie sich an. Alle Kunden, Angebote, Produkte und Einstellungen sollten genau so sein, wie sie zum Backup-Zeitpunkt waren.

Getestete Notfallwiederherstellung

Der vollständige Weg vom Backup bis zur isolierten Wiederherstellung wird durchgängig als automatisierte Notfallwiederherstellungs-Übung getestet (Szenarien A–G): Wiederherstellung mit gleichem Schlüssel, Rekey auf einen anderen Schlüssel, Bestätigung von Klartext-PII, Zurücksetzen betrieblicher Geheimnisse, Ablehnung bei falscher Identität und Rollback nach der Wiederherstellung. Sie läuft gegen ein echtes PostgreSQL in einer Wegwerf-Umgebung und prüft die oben beschriebenen Rollback- und Schlüsselgarantien.

Backup-Aufbewahrung

  • Lokal: Die BACKUP_RETENTION_DAILY (Standard: 7) neuesten erfolgreichen Backups werden aufbewahrt.
  • Remote: Konfigurieren Sie Lifecycle-Richtlinien bei Ihrem Speicheranbieter.
  • PII-entschlüsselte Backups sollten aufgrund ihrer sensiblen Natur eine strengere Aufbewahrung haben.

Backup-Timeout

Jedes Backup hat ein konfigurierbares Timeout (Standard: 30 Minuten über BACKUP_TIMEOUT_MINUTES). Wird es überschritten, wird das Backup beendet und als FAILED erfasst.

Empfohlene Strategie

  1. Aktivieren Sie tägliche automatische Backups mit AGE_RECIPIENT-Verschlüsselung und Remote-Speicher.
  2. Generieren Sie ein Recovery-Kit während der Einrichtung und bewahren Sie es an einem sicheren Offline-Ort auf.
  3. Verifizieren Sie Ihr Recovery-Kit gegen ein echtes Backup mit den mitgelieferten Verifizierungsskripten.
  4. Testen Sie die Wiederherstellungsverfahren regelmäßig — ein Backup, das nie getestet wurde, ist kein Backup.
  5. Laden Sie mindestens monatlich ein Backup an einen Offline-Ort herunter (erfordert Step-up-Autorisierung).
  6. Überwachen Sie den Backup-Status im Admin-Panel — Warnungen erscheinen für veraltete, fehlgeschlagene oder unverschlüsselte Backups.
  7. Bewahren Sie mindestens 7 Tage Backup-Historie auf (der Standard).

Last reviewed: Recently