Add validated Paperless restore test pattern
This commit is contained in:
@@ -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