Add validated Paperless restore test pattern
This commit is contained in:
@@ -393,7 +393,7 @@ Wenn weder externer Mirror noch lokaler Clone verfuegbar sind, ist `services/git
|
||||
- Unraid-USB-/Flash-Backup pruefen
|
||||
- Borg-Passphrase extern sicher hinterlegen
|
||||
- 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
|
||||
|
||||
---
|
||||
|
||||
@@ -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.
|
||||
- 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
|
||||
|
||||
- `KOMODO_WEBHOOK_SECRET` von `KOMODO_SECRET_KEY` getrennt und als eigene Stack-ENV-Variable dokumentiert.
|
||||
|
||||
@@ -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 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 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 |
|
||||
| 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 |
|
||||
@@ -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:
|
||||
|
||||
1. `paperless-ngx`
|
||||
2. `gitea`
|
||||
3. `vaultwarden`
|
||||
1. `mail-archiver`
|
||||
2. `paperless-ngx`
|
||||
3. `gitea`
|
||||
4. `vaultwarden`
|
||||
|
||||
Sie liefern hohen Erkenntnisgewinn ohne den kompletten Homelab-Neuaufbau zu brauchen.
|
||||
|
||||
@@ -26,7 +26,9 @@ Ziel:
|
||||
- `gitea-restore-test.ps1`: Gitea-Mini-Restore-Ablauf
|
||||
- `gitea-plan.md`: konkreter Gitea-Testplan
|
||||
- `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
|
||||
|
||||
@@ -50,14 +52,17 @@ Stand nach dem ersten echten Vaultwarden-Test:
|
||||
|
||||
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
|
||||
|
||||
Aktuell ist das erste validierte Muster vorhanden.
|
||||
|
||||
- echter Vaultwarden-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
|
||||
- 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.
|
||||
|
||||
@@ -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
|
||||
@@ -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."
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user