1 Restore Paperless
SurfaceScratcher edited this page 2026-05-17 22:05:49 +02:00

Restore: Paperless-ngx

Backup- und Restore-Pfad für die Paperless-ngx-Instanz auf paperless.mrrm.de (Host prod-alt).

Backup-Setup

  • Repo: sftp:qnap-backup:/share/Backup - Server/restic-repo (geteiltes restic-Repo, alle Hosts; Snapshots per --host-Tag separiert)
  • Wrapper: /usr/local/bin/resticq (setzt sftp.command für die QNAP, die kein registriertes SFTP-Subsystem hat)
  • Timer: restic-backup.timer (täglich 03:30)
  • Pre-Hook: /etc/restic-backup/pre.d/10-paperless-export.sh ruft document_exporter im Container paperless-paperless-webserver-1 auf und schreibt den portablen Export nach /usr/src/paperless/export (gemountet als Docker-Volume paperless_paperless-export)
  • Gesichertes Volume: /var/lib/docker/volumes/paperless_paperless-export/_data (Teil von nas_backup_dirs in inventory/host_vars/prod-alt/main.yml)

Der Export enthält manifest.json (Django-Fixtures aller Documents inkl. MD5-Checksummen), metadata.json, sowie die Verzeichnisse originals/, archive/, thumbnails/.

Restore-Test (Hash-Verify)

Verifiziert das Backup ohne den Live-Stack zu berühren. Läuft auf dev-neu gegen den prod-alt-Snapshot.

# 1. Neuesten prod-alt-Snapshot mit Paperless-Export finden
. /etc/restic.env
SNAP=$(resticq snapshots \
  --host prod-alt \
  --path /var/lib/docker/volumes/paperless_paperless-export/_data \
  --json | jq -r '.[-1].id')

# 2. Restore in Temp-Dir
TARGET=/tmp/restore-test-paperless-$(date +%Y%m%d-%H%M%S)
resticq restore "$SNAP" \
  --target "$TARGET" \
  --include /var/lib/docker/volumes/paperless_paperless-export/_data

EXPORT="$TARGET/var/lib/docker/volumes/paperless_paperless-export/_data"

# 3. Hash-Verify: alle MD5s aus manifest.json gegen die restored Files matchen
python3 - "$EXPORT" <<'PY'
import json, hashlib, os, sys
root = sys.argv[1]
m = json.load(open(os.path.join(root, "manifest.json")))
docs = [x["fields"] for x in m if x.get("model") == "documents.document"]
expected_orig = {d["checksum"] for d in docs if d.get("checksum")}
expected_arch = {d["archive_checksum"] for d in docs if d.get("archive_checksum")}
def md5(p):
    h = hashlib.md5()
    with open(p, "rb") as f:
        for c in iter(lambda: f.read(65536), b""): h.update(c)
    return h.hexdigest()
def check(subdir, expected):
    d = os.path.join(root, subdir)
    if not os.path.isdir(d): return (0, 0, len(expected))
    files = [os.path.join(d, f) for f in os.listdir(d)]
    found = {md5(p) for p in files}
    return (len(files), len(expected & found), len(expected - found))
o = check("originals", expected_orig)
a = check("archive",   expected_arch)
print(f"docs={len(docs)}")
print(f"originals: files={o[0]} matched={o[1]} missing={o[2]}")
print(f"archive:   files={a[0]} matched={a[1]} missing={a[2]}")
print("OVERALL:", "PASS" if (o[2]==0 and a[2]==0) else "FAIL")
PY

# 4. Cleanup
rm -rf "$TARGET"

Letzter erfolgreicher Restore-Test

Datum Snapshot Docs Originals matched Archive matched Ergebnis
2026-05-17 0f73d181 (2026-05-17 02:38 prod-alt) 1 1/1 1/1 PASS

Echter Restore in Produktion

Wenn der prod-alt-Paperless-Stack verloren geht:

  1. Stack stoppen (falls noch läuft):

    cd /opt/server-stack/hosts/prod-alt/paperless && docker compose down
    
  2. Neusten Export aus dem Backup restoren:

    . /etc/restic.env
    SNAP=$(resticq snapshots --host prod-alt \
      --path /var/lib/docker/volumes/paperless_paperless-export/_data \
      --json | jq -r '.[-1].id')
    resticq restore "$SNAP" \
      --target /tmp/paperless-restore \
      --include /var/lib/docker/volumes/paperless_paperless-export/_data
    
  3. Stack neu starten (legt leere Volumes an):

    docker compose up -d
    
  4. Restored Export ins Volume kopieren:

    docker cp /tmp/paperless-restore/var/lib/docker/volumes/paperless_paperless-export/_data/. \
      paperless-paperless-webserver-1:/usr/src/paperless/export/
    
  5. document_importer im Container ausführen:

    docker exec paperless-paperless-webserver-1 document_importer \
      --no-progress-bar /usr/src/paperless/export
    

    document_importer legt alle Dokumente, Tags, Korrespondenten, User und Permissions neu an.

  6. Verifikation: Web-UI auf https://paperless.mrrm.de öffnen, Document-Count abgleichen.

Referenzen