Add validated Paperless restore test pattern

This commit is contained in:
2026-05-07 11:01:27 +02:00
parent d351b1cac8
commit 2cc39c73f6
8 changed files with 309 additions and 7 deletions
+1 -1
View File
@@ -393,7 +393,7 @@ Wenn weder externer Mirror noch lokaler Clone verfuegbar sind, ist `services/git
- Unraid-USB-/Flash-Backup pruefen - Unraid-USB-/Flash-Backup pruefen
- Borg-Passphrase extern sicher hinterlegen - Borg-Passphrase extern sicher hinterlegen
- Komodo Stack-ENV-Werte zentral ausserhalb von Komodo dokumentieren - Komodo Stack-ENV-Werte zentral ausserhalb von Komodo dokumentieren
- Restore-Smoke-Test fuer mindestens einen weiteren kritischen Dienst nach Vaultwarden und Gitea dokumentieren - regelmaessige automatisierte Restore-Smoke-Tests fuer Vaultwarden, Gitea und Paperless etablieren
- `komodo-mongo`-Dump nach Major-Upgrades gezielt kontrollieren - `komodo-mongo`-Dump nach Major-Upgrades gezielt kontrollieren
--- ---
+10
View File
@@ -36,6 +36,16 @@ Dieses Dokument ist nur noch ein historischer Verlauf. Der aktuelle operative Ab
- Report wurde unter `/mnt/user/backups/restore-reports/gitea-2026-05-07.md` geschrieben. - Report wurde unter `/mnt/user/backups/restore-reports/gitea-2026-05-07.md` geschrieben.
- Testdaten unter `/mnt/user/backups/restore-lab/gitea/data` wurden nach erfolgreichem Lauf wieder bereinigt. - Testdaten unter `/mnt/user/backups/restore-lab/gitea/data` wurden nach erfolgreichem Lauf wieder bereinigt.
### 2026-05-07 - Paperless Restore-Test praktisch verifiziert
- Erster echter Paperless-Mini-Restore gegen das produktive Borg-Repo `hetzner_borg_appdata_critical` erfolgreich durchgefuehrt.
- Restore umfasste sowohl die Dateipfade als auch `postgresql17-paperless.dump` aus dem Borg-Archiv.
- Testinstanzen `restoretest-paperless`, `restoretest-paperless-postgres` und `restoretest-paperless-redis` liefen isoliert ohne Traefik.
- Login-Seite war lokal auf `127.0.0.1:18120` erreichbar.
- Der Dump-Import in Test-Postgres war erfolgreich; die Test-Datenbank enthielt `25` Dokumente.
- Report wurde unter `/mnt/user/backups/restore-reports/paperless-2026-05-07.md` geschrieben.
- Testdaten unter `/mnt/user/backups/restore-lab/paperless` wurden nach erfolgreichem Lauf wieder bereinigt.
### 2026-05-06 - Komodo Webhook Secret getrennt ### 2026-05-06 - Komodo Webhook Secret getrennt
- `KOMODO_WEBHOOK_SECRET` von `KOMODO_SECRET_KEY` getrennt und als eigene Stack-ENV-Variable dokumentiert. - `KOMODO_WEBHOOK_SECRET` von `KOMODO_SECRET_KEY` getrennt und als eigene Stack-ENV-Variable dokumentiert.
+5 -4
View File
@@ -42,7 +42,7 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`.
| Dienst | Fuehrende Quelle | Datei-Restore | Dump / DB | Secrets / ENV | Abhaengigkeiten | Smoke-Test | | Dienst | Fuehrende Quelle | Datei-Restore | Dump / DB | Secrets / ENV | Abhaengigkeiten | Smoke-Test |
|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|
| Paperless-ngx | Borg + Dumps | `/mnt/user/appdata/paperless-ngx/data`, `/mnt/user/documents/paperless`, `/mnt/user/documents/paperless/export`, `/mnt/user/documents/scans_inbox` | `postgresql17-paperless.dump` | `PAPERLESS_DBPASS`, `PAPERLESS_REDIS` | PostgreSQL 17, Redis, Traefik | Web-UI startet, Dokumente vorhanden | | Paperless-ngx | Borg + Dumps | `/mnt/user/appdata/paperless-ngx/data`, `/mnt/user/documents/paperless`, `/mnt/user/documents/paperless/export`, `/mnt/user/documents/scans_inbox` | `postgresql17-paperless.dump` | `PAPERLESS_DBPASS`, `PAPERLESS_REDIS`, `borg_repo_passphrase.txt` fuer Restore-Tests | PostgreSQL 17, Redis, Traefik | Web-UI startet, Dokumente vorhanden; Mini-Restore nach `/mnt/user/backups/restore-lab/paperless` am 2026-05-07 erfolgreich validiert |
| Mealie | Borg + Dump | `/mnt/user/appdata/mealie/data`, optional `/mnt/user/appdata/mealie/postgres` bei lokalem Share-Weiterbetrieb | `mealie.dump` | `mealie_postgres_password.txt` | `mealie-postgres`, Traefik | UI startet, Rezepte vorhanden | | Mealie | Borg + Dump | `/mnt/user/appdata/mealie/data`, optional `/mnt/user/appdata/mealie/postgres` bei lokalem Share-Weiterbetrieb | `mealie.dump` | `mealie_postgres_password.txt` | `mealie-postgres`, Traefik | UI startet, Rezepte vorhanden |
| Immich | Borg + Dump | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive` | `immich.dump` | `IMMICH_DB_PASSWORD`, `immich_postgres_password.txt` | `immich_postgres`, `immich_redis`, Traefik | UI startet, Medienbibliothek sichtbar | | Immich | Borg + Dump | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive` | `immich.dump` | `IMMICH_DB_PASSWORD`, `immich_postgres_password.txt` | `immich_postgres`, `immich_redis`, Traefik | UI startet, Medienbibliothek sichtbar |
| Mail-Archiver | Borg + Shared Dump | `/mnt/user/appdata/mailarchiver/data-protection-keys` | `postgresql17-mailarchiver.dump` | `MAILARCHIVER_DB_CONNECTION`, `MAILARCHIVER_AUTH_PASSWORD` | PostgreSQL 17, Traefik, Authelia | Authelia-Weiterleitung greift; nach Login startet die Web-UI und das Archiv laesst sich oeffnen | | Mail-Archiver | Borg + Shared Dump | `/mnt/user/appdata/mailarchiver/data-protection-keys` | `postgresql17-mailarchiver.dump` | `MAILARCHIVER_DB_CONNECTION`, `MAILARCHIVER_AUTH_PASSWORD` | PostgreSQL 17, Traefik, Authelia | Authelia-Weiterleitung greift; nach Login startet die Web-UI und das Archiv laesst sich oeffnen |
@@ -110,8 +110,9 @@ Die Dump-Erzeugung ist host-seitig ueber `ops/borg-ui/scripts/pre-backup-dumps.s
Wenn weitere Restore-Uebungen dokumentiert werden sollen, sind diese Dienste besonders geeignet: Wenn weitere Restore-Uebungen dokumentiert werden sollen, sind diese Dienste besonders geeignet:
1. `paperless-ngx` 1. `mail-archiver`
2. `gitea` 2. `paperless-ngx`
3. `vaultwarden` 3. `gitea`
4. `vaultwarden`
Sie liefern hohen Erkenntnisgewinn ohne den kompletten Homelab-Neuaufbau zu brauchen. Sie liefern hohen Erkenntnisgewinn ohne den kompletten Homelab-Neuaufbau zu brauchen.
+7 -2
View File
@@ -26,7 +26,9 @@ Ziel:
- `gitea-restore-test.ps1`: Gitea-Mini-Restore-Ablauf - `gitea-restore-test.ps1`: Gitea-Mini-Restore-Ablauf
- `gitea-plan.md`: konkreter Gitea-Testplan - `gitea-plan.md`: konkreter Gitea-Testplan
- `gitea-compose.test.yml`: isolierte Testinstanz fuer Gitea - `gitea-compose.test.yml`: isolierte Testinstanz fuer Gitea
- spaeter weiterer Test fuer `paperless` - `paperless-restore-test.ps1`: Paperless-Mini-Restore-Ablauf
- `paperless-plan.md`: konkreter Paperless-Testplan
- `paperless-compose.test.yml`: isolierte Testinstanz fuer Paperless inkl. Test-Postgres und Test-Redis
## Automatisierungsmodell ## Automatisierungsmodell
@@ -50,14 +52,17 @@ Stand nach dem ersten echten Vaultwarden-Test:
Das ist das bevorzugte Muster fuer weitere dateibasierte Restore-Tests wie `gitea`. Das ist das bevorzugte Muster fuer weitere dateibasierte Restore-Tests wie `gitea`.
Fuer datenbankgestuetzte Dienste wie `paperless` kommt zusaetzlich ein isolierter Dump-Restore in Test-Postgres dazu.
## Status ## Status
Aktuell ist das erste validierte Muster vorhanden. Aktuell ist das erste validierte Muster vorhanden.
- echter Vaultwarden-Restore am 2026-05-07 erfolgreich verifiziert - echter Vaultwarden-Restore am 2026-05-07 erfolgreich verifiziert
- echter Gitea-Restore am 2026-05-07 erfolgreich verifiziert - echter Gitea-Restore am 2026-05-07 erfolgreich verifiziert
- echter Paperless-Restore am 2026-05-07 erfolgreich verifiziert
- Restore-Lab und Report-Pfade auf dem Host angelegt - Restore-Lab und Report-Pfade auf dem Host angelegt
- V1-Ablauf weiter ohne `ntfy`, mit Bereinigung nach Erfolg - V1-Ablauf weiter ohne `ntfy`, mit Bereinigung nach Erfolg
- `paperless` ist der naechste Referenz-Restore - naechster grosser Kandidat ist ein weiterer datenbankgestuetzter Dienst oder die Automatisierung
Vor dem ersten echten Testlauf muessen Zielpfade, Quellpfade und Bereinigungsschritte bewusst freigegeben werden. Vor dem ersten echten Testlauf muessen Zielpfade, Quellpfade und Bereinigungsschritte bewusst freigegeben werden.
@@ -0,0 +1,61 @@
services:
restoretest-paperless-postgres:
image: postgres:17.9@sha256:5b96f1a16bd9768b060dd2ffe55cb6225c4d9ef4d214a8b21eb08134869a97e4
container_name: restoretest-paperless-postgres
restart: "no"
environment:
TZ: Europe/Berlin
POSTGRES_USER: paperless
POSTGRES_DB: paperless
POSTGRES_PASSWORD: restoretest-paperless-db
PGDATA: /var/lib/postgresql/data
volumes:
- /mnt/user/backups/restore-lab/paperless/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U paperless -d paperless"]
interval: 10s
timeout: 5s
retries: 10
security_opt:
- no-new-privileges:true
restoretest-paperless-redis:
image: redis:7-alpine
container_name: restoretest-paperless-redis
restart: "no"
command:
- sh
- -c
- exec redis-server --appendonly yes --requirepass "restoretest-paperless-redis"
security_opt:
- no-new-privileges:true
restoretest-paperless:
image: ghcr.io/paperless-ngx/paperless-ngx:2.20.10@sha256:07a0b4ba01ce377c82a0636e16c0c3d931fde5b7e9304de6601986cc42d9b6e6
container_name: restoretest-paperless
restart: "no"
depends_on:
restoretest-paperless-postgres:
condition: service_healthy
restoretest-paperless-redis:
condition: service_started
environment:
PAPERLESS_TIKA_ENABLED: "0"
PAPERLESS_DBENGINE: postgresql
PAPERLESS_DBHOST: restoretest-paperless-postgres
PAPERLESS_DBNAME: paperless
PAPERLESS_DBUSER: paperless
PAPERLESS_DBPASS: restoretest-paperless-db
PAPERLESS_REDIS: redis://:restoretest-paperless-redis@restoretest-paperless-redis:6379
PAPERLESS_TIME_ZONE: Europe/Berlin
PAPERLESS_OCR_LANGUAGE: deu+eng
PAPERLESS_URL: http://127.0.0.1:18120
ports:
- "127.0.0.1:18120:8000"
volumes:
- /mnt/user/backups/restore-lab/paperless/consume:/usr/src/paperless/consume
- /mnt/user/backups/restore-lab/paperless/data:/usr/src/paperless/data
- /mnt/user/backups/restore-lab/paperless/export:/usr/src/paperless/export
- /mnt/user/backups/restore-lab/paperless/media:/usr/src/paperless/media
security_opt:
- no-new-privileges:true
+72
View File
@@ -0,0 +1,72 @@
# Paperless Restore Test Plan
## Ziel
Nachweisen, dass ein Paperless-Backup in einer isolierten Testumgebung wieder startbar ist und sowohl Dokumentenpfade als auch PostgreSQL-Dump sauber zusammenlaufen.
## Quelle
- Backup-Quelle: Borg / Share-Backup
- fachlich relevante Dateipfade:
- `/mnt/user/appdata/paperless-ngx/data`
- `/mnt/user/documents/paperless`
- `/mnt/user/documents/paperless/export`
- `/mnt/user/documents/scans_inbox`
- fachlich relevanter Dump:
- `/mnt/user/backups/borg/dumps/latest/postgresql17-paperless.dump`
## Test-Ziel
- Restore-Lab: `/mnt/user/backups/restore-lab/paperless`
- Testdatenpfade:
- `/mnt/user/backups/restore-lab/paperless/data`
- `/mnt/user/backups/restore-lab/paperless/media`
- `/mnt/user/backups/restore-lab/paperless/export`
- `/mnt/user/backups/restore-lab/paperless/consume`
- `/mnt/user/backups/restore-lab/paperless/postgres`
- Testcontainer:
- `restoretest-paperless`
- `restoretest-paperless-postgres`
- `restoretest-paperless-redis`
- Testport Web: `127.0.0.1:18120:8000`
- Report-Ziel: `/mnt/user/backups/restore-reports/paperless-YYYY-MM-DD.md`
## Schutzregeln
- produktive Pfade nie beschreiben
- produktive Domain `paperless.kaleschke.info` nicht fuer die Testinstanz uebernehmen
- keine Traefik-Labels fuer die Testinstanz
- keine produktive PostgreSQL- oder Redis-Instanz fuer den Test verwenden
- Testcontainer nur gegen Restore-Lab-Daten und isolierte Test-Backends starten
## Geplanter Ablauf
1. Restore-Ziel unter `/mnt/user/backups/restore-lab/paperless` vorbereiten
2. Paperless-Dateipfade aus Borg in das Restore-Lab wiederherstellen
3. Test-Postgres und Test-Redis mit `ops/restore-tests/paperless-compose.test.yml` starten
4. `postgresql17-paperless.dump` in Test-Postgres importieren
5. Testinstanz `restoretest-paperless` starten
6. lokalen Smoke-Test gegen `http://127.0.0.1:18120` ausfuehren
7. Report unter `/mnt/user/backups/restore-reports/` schreiben
8. Testcontainer stoppen und Testumgebung bereinigen
## Smoke-Test
Minimal erfolgreich:
- Test-Postgres startet
- Dump-Import gelingt
- Paperless-Web-UI antwortet
- mindestens ein Dokument liegt im Restore-Lab-Medienpfad
Optional spaeter:
- Login-Seite gezielt pruefen
- Dokumentanzahl aus UI oder DB querpruefen
- OCR-/Task-Worker-Status verifizieren
## Noch offen vor dem ersten echten Lauf
- exakter Borg-Restore-Befehl fuer alle vier Dateipfade
- exakter `pg_restore`-Befehl im Test-Postgres
- wie stark wir `consume` im ersten Lauf ueberhaupt brauchen
@@ -0,0 +1,38 @@
param(
[string]$BackupSource = "/mnt/user/backups/borg",
[string]$DumpSource = "/mnt/user/backups/borg/dumps/latest/postgresql17-paperless.dump",
[string]$RestoreRoot = "/mnt/user/backups/restore-lab/paperless",
[string]$ReportRoot = "/mnt/user/backups/restore-reports",
[string]$BorgPassphraseFile = "/mnt/user/appdata/secrets/borg_repo_passphrase.txt",
[switch]$WhatIf
)
$Mode = if ($WhatIf) { "WhatIf" } else { "PlanOnly" }
Write-Output "Paperless restore test scaffold"
Write-Output "BackupSource: $BackupSource"
Write-Output "DumpSource: $DumpSource"
Write-Output "RestoreRoot: $RestoreRoot"
Write-Output "ReportRoot: $ReportRoot"
Write-Output "BorgPassphraseFile: $BorgPassphraseFile"
Write-Output "Expected Borg source paths inside archive:"
Write-Output " - local/appdata/paperless-ngx/data"
Write-Output " - local/paperless/media"
Write-Output " - local/paperless/export"
Write-Output " - local/paperless/consume"
Write-Output "Mode: $Mode"
Write-Output ""
Write-Output "Planned steps:"
Write-Output "1. Prepare restore-lab target under /mnt/user/backups/restore-lab/paperless"
Write-Output "2. Restore Paperless file data into isolated test paths"
Write-Output ' Template: borg extract "$BORG_REPO" "::ARCHIVE_NAME" local/appdata/paperless-ngx/data local/paperless/media local/paperless/export local/paperless/consume'
Write-Output ' Passphrase source: $(cat /mnt/user/appdata/secrets/borg_repo_passphrase.txt)'
Write-Output "3. Start isolated test Postgres and test Redis"
Write-Output "4. Import /mnt/user/backups/borg/dumps/latest/postgresql17-paperless.dump into test Postgres"
Write-Output "5. Start restoretest-paperless against restored files and isolated DB/Redis"
Write-Output "6. Run smoke checks against the local web endpoint"
Write-Output "7. Write markdown report under /mnt/user/backups/restore-reports"
Write-Output "8. Stop test containers and clean restore data after success"
Write-Output ""
Write-Output "This script is intentionally a scaffold only."
Write-Output "No restore, no dump import, no container start, no file write is executed yet."
+115
View File
@@ -0,0 +1,115 @@
# Paperless Restore Runbook
## Vorbedingungen
- Borg-Quelle ist verfuegbar
- Borg-Passphrase-Datei vorhanden: `/mnt/user/appdata/secrets/borg_repo_passphrase.txt`
- aktueller Dump vorhanden: `/mnt/user/backups/borg/dumps/latest/postgresql17-paperless.dump`
- Testpfade unter `/mnt/user/backups/restore-lab/` und `/mnt/user/backups/restore-reports/` sind freigegeben
## Bestaetigter Host-Stand
- produktive Paperless-Daten liegen unter `/mnt/user/appdata/paperless-ngx/data`
- produktive Medien liegen unter `/mnt/user/documents/paperless`
- produktiver Exportpfad liegt unter `/mnt/user/documents/paperless/export`
- produktiver Consume-Pfad liegt unter `/mnt/user/documents/scans_inbox`
- aktueller Dump `postgresql17-paperless.dump` ist vorhanden
## Bestaetigter Teststand
- echter Mini-Restore am `2026-05-07` erfolgreich gelaufen
- Datei-Restore und Dump kamen aus dem produktiven Borg-Archiv
- Testcontainer:
- `restoretest-paperless`
- `restoretest-paperless-postgres`
- `restoretest-paperless-redis`
- Login-Seite war lokal auf `127.0.0.1:18120` erreichbar
- Dump-Import in Test-Postgres war erfolgreich
- Test-Datenbank enthielt `25` Dokumente
- Report liegt unter `/mnt/user/backups/restore-reports/paperless-2026-05-07.md`
## Platzhalter
- `ARCHIVE_NAME`: Borg-Archiv fuer den Restore-Test
- `REPORT_DATE`: z. B. `2026-05-07`
- `BORG_REPO`: Host-Borg-Repo
- `BORG_PASSPHRASE_FILE`: `/mnt/user/appdata/secrets/borg_repo_passphrase.txt`
## Ablauf
1. Testpfade vorbereiten
```bash
mkdir -p /mnt/user/backups/restore-lab/paperless/{data,media,export,consume,postgres}
mkdir -p /mnt/user/backups/restore-reports
rm -rf /mnt/user/backups/restore-lab/paperless/data/*
rm -rf /mnt/user/backups/restore-lab/paperless/media/*
rm -rf /mnt/user/backups/restore-lab/paperless/export/*
rm -rf /mnt/user/backups/restore-lab/paperless/consume/*
rm -rf /mnt/user/backups/restore-lab/paperless/postgres/*
```
2. Paperless-Dateipfade aus Borg in das Restore-Lab extrahieren
```bash
export BORG_REPO='...'
export BORG_PASSPHRASE="$(cat /mnt/user/appdata/secrets/borg_repo_passphrase.txt)"
borg list "$BORG_REPO"
cd /mnt/user/backups/restore-lab/paperless
borg extract "$BORG_REPO" "::ARCHIVE_NAME" local/appdata/paperless-ngx/data local/paperless/media local/paperless/export local/paperless/consume
mv /mnt/user/backups/restore-lab/paperless/local/appdata/paperless-ngx/data /mnt/user/backups/restore-lab/paperless/data
mv /mnt/user/backups/restore-lab/paperless/local/paperless/media /mnt/user/backups/restore-lab/paperless/media
mv /mnt/user/backups/restore-lab/paperless/local/paperless/export /mnt/user/backups/restore-lab/paperless/export
mv /mnt/user/backups/restore-lab/paperless/local/paperless/consume /mnt/user/backups/restore-lab/paperless/consume
```
3. Test-Postgres und Test-Redis starten
```bash
docker compose -f /mnt/user/services/homelab/ops/restore-tests/paperless-compose.test.yml up -d restoretest-paperless-postgres restoretest-paperless-redis
```
4. Dump in Test-Postgres importieren
```bash
docker exec -i restoretest-paperless-postgres pg_restore -U paperless -d paperless < /mnt/user/backups/borg/dumps/latest/postgresql17-paperless.dump
```
5. Testinstanz starten
```bash
docker compose -f /mnt/user/services/homelab/ops/restore-tests/paperless-compose.test.yml up -d restoretest-paperless
```
6. Smoke-Test
```bash
curl -I http://127.0.0.1:18120
docker logs restoretest-paperless --tail 50
find /mnt/user/backups/restore-lab/paperless/media -type f | head -n 10
```
Minimal erfolgreich:
- Dump-Import gelingt
- HTTP-Antwort kommt
- Dokumentenpfad ist im Restore-Lab befuellt
7. Testcontainer wieder stoppen
```bash
docker compose -f /mnt/user/services/homelab/ops/restore-tests/paperless-compose.test.yml down
```
8. Testdaten nach erfolgreichem Lauf bereinigen
```bash
rm -rf /mnt/user/backups/restore-lab/paperless
```
## Festgelegte Entscheidungen
- Paperless nutzt fuer Restore-Tests immer isoliertes Test-Postgres und Test-Redis.
- Testdaten werden nach erfolgreichem Lauf geloescht.
- `ntfy` wird nicht im ersten echten Lauf eingebunden.
- Die Borg-Passphrase wird fuer Restore-Tests aus einer Host-Secret-Datei gelesen, nicht aus Borg-UI-Interna.