DarkWolfCave
docker

Paperless-NGX sichern - Backup und Restore Anleitung

Wolf bewacht einen Aktenschrank mit Backup-Symbol - Paperless-NGX Backup auf dem Raspberry Pi
DarkWolf denkt nachKI-BildGeneriert mit Gemini

Paperless-NGX Backup mit Docker - Dokumente sicher auf dem Raspberry Pi

Paperless-NGX läuft, hunderte Dokumente sind gescannt und getaggt - aber was passiert, wenn ein Update schiefgeht oder du auf neue Hardware umziehen willst? Ich zeige dir, wie du ein sauberes, automatisiertes Backup einrichtest und im Ernstfall alles wiederherstellst.

DarkWolfCave.de

🔄 Update August 2026: Das Restore-Test-Script wurde überarbeitet. Die alte Version hat die Produktions-docker-compose.yml kopiert - bei Bind-Mounts lief der Test gegen echte Daten. Außerdem fehlten PAPERLESS_SECRET_KEY (Paperless 3.x) und eine Export-Kopie für den Test (direkter :ro-Mount führte zu That path doesn't appear to be readable). Das neue Script generiert eine isolierte Compose mit Named Volumes, kopiert den Export nach /tmp, wartet auf den Healthcheck und importiert nur die Kopie. Wenn du das alte Script nutzt, tausche es gegen die aktuelle Version weiter unten aus. Backup-Script und manueller Restore sind nicht betroffen.

Wer Paperless-NGX auf dem Raspberry Pi betreibt, speichert die Daten in der Regel nicht auf der SD-Karte, sondern auf einer NVMe-SSD, einer externen Festplatte oder einer NAS-Freigabe. Damit sind die Dokumente selbst schon auf einem zuverlässigeren Speichermedium - und dein NAS hat vermutlich ein eigenes Backup.

Trotzdem reicht ein reines Datei-Backup nicht aus. Die PostgreSQL-Datenbank läuft in einem Docker-Container, und ein Dateikopie bei laufender DB kann inkonsistent sein. Außerdem speichert Paperless-NGX die Zuordnungen zwischen Dokumenten, Tags und Korrespondenten in der Datenbank - ohne diese Metadaten sind deine PDFs nur eine Sammlung loser Dateien. Und spätestens beim Umzug auf neue Hardware brauchst du einen sauberen Export-Import-Pfad.

In meinem Artikel zur Paperless-NGX Installation mit Docker ging es um das Einrichten. Hier geht es um das Absichern - mit dem eingebauten Document Exporter, einem separaten Datenbank-Dump und einem Cron-Job, der beides automatisch erledigt.

Was genau muss gesichert werden?

Paperless-NGX speichert Daten an mehreren Stellen. Für ein vollständiges Backup musst du alle davon berücksichtigen:

KomponenteInhaltWo gespeichert
OriginaldokumenteHochgeladene PDFs, Bilder, ScansVolume media → Unterordner documents/originals/
Archiv-PDFsOCR-verarbeitete VersionenVolume media → Unterordner documents/archive/
ThumbnailsVorschaubilder für die Web-GUIVolume media → Unterordner documents/thumbnails/
PostgreSQL-DatenbankTags, Korrespondenten, Dokumenttypen, Benutzer, ZuordnungenVolume pgdata
Konfigurationdocker-compose.yml, .env-DateiAuf dem Host im Projektverzeichnis

Wenn du Paperless-NGX wie in meiner Installationsanleitung eingerichtet hast, liegen die Daten als Bind-Mounts auf dem Host. Die genauen Pfade findest du in deiner docker-compose.yml.

Zwei Backup-Strategien im Vergleich

Es gibt zwei Wege, Paperless-NGX zu sichern. Beide haben ihre Berechtigung:

Document Exporter (Paperless-eigenes Tool)

Der Document Exporter ist ein Management-Command von Paperless-NGX. Er exportiert alle Dokumente, Thumbnails und eine manifest.json mit sämtlichen Metadaten. Der passende Gegenpart - der Document Importer - stellt aus diesem Export eine komplette Paperless-Instanz wieder her.

Vorteile:

  • Offiziell unterstützt und dokumentiert
  • Plattformunabhängig - du kannst den Export auf einem anderen System importieren
  • Inkrementelle Exports möglich (nur geänderte Dateien)

Nachteile:

  • Bei großen Dokumentenbeständen langsamer als ein Dateibackup
  • Restore nur auf einer leeren Installation

Dateibasiertes Backup (Volumes + DB-Dump)

Hier sicherst du die Docker-Volumes direkt vom Host und erstellst einen separaten PostgreSQL-Dump. Das ist schneller, aber an die gleiche Verzeichnisstruktur gebunden.

Vorteile:

  • Schnell, auch bei tausenden Dokumenten
  • Datenbank-Dump ist kompakt und einzeln wiederherstellbar

Nachteile:

  • Nicht portabel - gleiche Verzeichnisstruktur auf dem Zielsystem nötig
  • Keine offizielle Validierung durch Paperless-NGX

Meine Empfehlung: Beides kombinieren. Der Document Exporter als primäres Backup, der Datenbank-Dump als schnelle Zusatzsicherung. Das Backup-Script weiter unten macht genau das.

VIP Support
Wolf Support Avatar

Du wirst hier einen groben Überblick finden.
Allerdings biete ich dir auch noch etwas mehr Support an:

  • Du benötigst persönlichen Support
  • Du möchtest von Beginn an Unterstützung bei deinem Projekt
  • Du möchtest ein hier vorgestelltes Plugin durch mich installieren und einrichten lassen
  • Du würdest gerne ein von mir erstelltes Script etwas mehr an deine Bedürfnisse anpassen

Für diese Punkte und noch einiges mehr habe ich einen limitierten VIP-Tarif eingerichtet.

Falls der Tarif gerade nicht verfügbar ist, kontaktiere mich auf Discord!

Voraussetzungen

Bevor du loslegst, prüfe diese Punkte:

  • Paperless-NGX läuft als Docker-Container auf deinem Raspberry Pi (siehe Installationsanleitung)
  • Du hast SSH-Zugriff auf den Pi
  • Genügend freier Speicherplatz für den Export - rechne mit dem 1,5- bis 2-fachen deiner Paperless-Daten
  • Ein separates Backup-Ziel: NAS-Freigabe, externe Festplatte oder ein zweiter Server

💡 Tipp: Auch wenn deine Paperless-Daten bereits auf einer NVMe-SSD oder einem NAS liegen - der Export und der Datenbank-Dump sollten auf ein anderes Gerät. Dein NAS-Backup sichert zwar die Dateien, aber nicht unbedingt die PostgreSQL-Datenbank in einem konsistenten Zustand.

Backup-Verzeichnis einrichten

Erstelle ein Verzeichnis für deine Backups. In diesem Beispiel nutze ich einen Ordner auf dem Host:

# Backup-Verzeichnis anlegen
mkdir -p ~/paperless-backup/export
mkdir -p ~/paperless-backup/database

Falls du eine externe Festplatte nutzt, mounte sie zuerst und passe den Pfad entsprechend an - z.B. /mnt/backup/paperless/.

Backup mit dem Document Exporter

Der Document Exporter ist das offizielle Backup-Tool von Paperless-NGX. Er erstellt eine vollständige Kopie aller Dokumente und Metadaten.

Manueller Export

Wechsle in dein Paperless-NGX Projektverzeichnis und führe den Exporter aus:

# In das Paperless-Verzeichnis wechseln
cd ~/paperless-ngx

# Document Exporter ausführen
docker compose exec -T webserver document_exporter ../export

Das -T Flag verhindert TTY-Fehler, die bei automatisierter Ausführung (Cron) auftreten würden. Der Exporter schreibt die Daten in das export-Volume, das in deiner docker-compose.yml definiert sein muss. Falls du das Volume noch nicht gemappt hast, ergänze es in deiner Compose-Datei:

services:
  webserver:
    volumes:
      - ./export:/usr/src/paperless/export

Ohne dieses Volume-Mapping landet der Export nur innerhalb des Containers und geht beim Neustart verloren.

Falls dir Volumes und Bind-Mounts noch nicht ganz klar sind: Die Grundlagen erkläre ich im Kurs Docker & Container-Praxis (29 €).

Was der Exporter erstellt

Nach dem Export findest du im Zielverzeichnis:

export/
├── manifest.json          # Alle Metadaten (Tags, Korrespondenten, etc.)
├── [Datum] [Korrespondent] [Titel].pdf          # Originaldokumente
├── [Datum] [Korrespondent] [Titel]_archive.pdf   # OCR-Versionen
└── [Datum] [Korrespondent] [Titel]_thumb.webp     # Thumbnails

Die manifest.json ist das Herzstück: Sie enthält alle Zuordnungen zwischen Dokumenten, Tags, Korrespondenten und Dokumenttypen. Ohne diese Datei sind die PDFs nur eine Sammlung loser Dateien.

Nützliche Export-Optionen

FlagFunktion
-pSortiert Dateien in Unterordner: archive/, originals/, thumbnails/, json/
-dLöscht Dateien aus dem Export-Verzeichnis, die in Paperless nicht mehr existieren
-cPrüft Checksummen statt Dateiattribute - genauer, aber langsamer
-naExportiert keine Archiv-PDFs (spart Platz, wenn du nur Originale brauchst)
-ntExportiert keine Thumbnails
-zKomprimiert den Export als ZIP-Datei (Importer akzeptiert ZIP direkt)
-smErstellt individuelle JSON-Dateien pro Dokument statt einer großen manifest.json
--passphraseVerschlüsselt sensible Felder (Mail-Passwörter, Tokens) mit AES - beim Import dieselbe Passphrase angeben
--no-progress-barUnterdrückt die Fortschrittsanzeige (für Scripts und Cron)

Für ein sauberes, inkrementelles Backup empfehle ich:

docker compose exec -T webserver document_exporter ../export -p -d --no-progress-bar

Das Flag -p sortiert die Dateien in Unterordner, -d räumt gelöschte Dokumente auf, und --no-progress-bar hält die Cron-Logs sauber.

Der Exporter arbeitet inkrementell: Er vergleicht den bestehenden Export mit der Datenbank und aktualisiert nur geänderte Dateien. Lösche das Export-Verzeichnis deshalb nicht manuell zwischen den Durchläufen - sonst muss der nächste Export alle Dokumente komplett neu schreiben.

PostgreSQL-Datenbank separat sichern

Der Document Exporter sichert die Metadaten in der manifest.json. Trotzdem erstelle ich zusätzlich einen separaten Datenbank-Dump. Der Grund: Ein SQL-Dump lässt sich schneller einspielen als ein vollständiger Document Import, und er dient als Fallback, falls mit dem Export etwas nicht stimmt.

Nutze dafür immer pg_dump über den laufenden Container - kopiere niemals die PostgreSQL-Dateien direkt aus dem Volume. Bei einer laufenden Datenbank kann das zu korrupten, unbrauchbaren Backups führen.

# PostgreSQL-Dump erstellen
docker compose exec -T db pg_dump -U paperless paperless > ~/paperless-backup/database/db_$(date +%Y%m%d_%H%M%S).sql

Passe den Benutzernamen (-U paperless) und den Datenbanknamen (paperless) an deine docker-compose.yml an. Die Werte stehen in den Umgebungsvariablen POSTGRES_USER und POSTGRES_DB.

⚠️ Wichtig: Der Datenbank-Dump enthält die Zugangsdaten im Klartext, die du in deiner .env-Datei oder docker-compose.yml definiert hast. Sichere die Dump-Dateien entsprechend ab - Leserechte nur für root.

Alte Dumps aufräumen

Damit sich die Datenbank-Dumps nicht stapeln, lösche regelmäßig ältere Versionen:

# Dumps älter als 30 Tage löschen
find ~/paperless-backup/database/ -name "db_*.sql" -mtime +30 -delete

Du willst Docker Backups nicht selbst einrichten? Ich übernehme das für dich —schau dir meine Services an

Automatisches Backup per Cron

Ein Backup, das du manuell anstoßen musst, vergisst du irgendwann. Ein Cron-Job erledigt das zuverlässig jede Nacht.

Backup-Script erstellen

Erstelle ein Script, das Document Export und Datenbank-Dump kombiniert:

#!/bin/bash
# Paperless-NGX Backup Script
# Kombiniert Document Exporter + PostgreSQL Dump + CronWolf Monitoring

# Konfiguration - an deine Pfade anpassen
PAPERLESS_DIR="$HOME/paperless-ngx"
BACKUP_DIR="$HOME/paperless-backup"
DB_BACKUP_DIR="$BACKUP_DIR/database"
LOG_FILE="$BACKUP_DIR/backup.log"
KEEP_DB_DAYS=30

# CronWolf Monitoring (optional - leer lassen wenn nicht genutzt)
# Den Token findest du in deinem CronWolf-Dashboard unter dem jeweiligen Check
CRONWOLF_UUID=""

# Zeitstempel
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# Fehlerzaehler - am Ende entscheidet dieser ob success oder fail
ERRORS=0

# Logging-Funktion
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

log "=== Backup gestartet ==="

# CronWolf: Start melden (misst die Laufzeit des Backups)
if [ -n "$CRONWOLF_UUID" ]; then
    curl -fsS --retry 3 "https://cronwolf.de/api/v2/ping/$CRONWOLF_UUID/start" > /dev/null 2>&1
fi

# In Paperless-Verzeichnis wechseln
cd "$PAPERLESS_DIR" || { log "FEHLER: Verzeichnis $PAPERLESS_DIR nicht gefunden"; ERRORS=$((ERRORS+1)); }

# 1. Document Exporter ausfuehren
log "Document Exporter laeuft..."
if docker compose exec -T webserver document_exporter ../export -p -d --no-progress-bar >> "$LOG_FILE" 2>&1; then
    log "Document Export erfolgreich"
    # Export ins Backup-Verzeichnis synchronisieren
    rsync -a --delete "$PAPERLESS_DIR/export/" "$BACKUP_DIR/export/"
    log "Export nach $BACKUP_DIR/export/ synchronisiert"
else
    log "FEHLER: Document Export fehlgeschlagen (Exit Code: $?)"
    ERRORS=$((ERRORS+1))
fi

# 2. PostgreSQL-Dump erstellen
log "Datenbank-Dump laeuft..."
DB_FILE="$DB_BACKUP_DIR/db_${TIMESTAMP}.sql"
if docker compose exec -T db pg_dump -U paperless paperless > "$DB_FILE" 2>> "$LOG_FILE"; then
    # Dump komprimieren
    gzip "$DB_FILE"
    log "Datenbank-Dump erstellt: db_${TIMESTAMP}.sql.gz"
else
    log "FEHLER: Datenbank-Dump fehlgeschlagen"
    ERRORS=$((ERRORS+1))
    rm -f "$DB_FILE"
fi

# 3. docker-compose.yml und .env sichern
cp "$PAPERLESS_DIR/docker-compose.yml" "$BACKUP_DIR/docker-compose.yml.backup" 2>/dev/null
cp "$PAPERLESS_DIR/.env" "$BACKUP_DIR/env.backup" 2>/dev/null
log "Konfigurationsdateien gesichert"

# 4. Alte Datenbank-Dumps aufräumen
DELETED=$(find "$DB_BACKUP_DIR" -name "db_*.sql.gz" -mtime +$KEEP_DB_DAYS -delete -print | wc -l)
log "Alte Dumps geloescht: $DELETED Dateien (aelter als $KEEP_DB_DAYS Tage)"

# 5. CronWolf benachrichtigen (falls konfiguriert)
if [ -n "$CRONWOLF_UUID" ]; then
    if [ "$ERRORS" -eq 0 ]; then
        # Erfolg: Basis-URL ohne Suffix
        curl -fsS --retry 3 "https://cronwolf.de/api/v2/ping/$CRONWOLF_UUID/" > /dev/null 2>&1
        log "CronWolf: success gemeldet"
    else
        curl -fsS --retry 3 "https://cronwolf.de/api/v2/ping/$CRONWOLF_UUID/fail" > /dev/null 2>&1
        log "CronWolf: fail gemeldet ($ERRORS Fehler)"
    fi
fi

log "=== Backup abgeschlossen (Fehler: $ERRORS) ==="

# Exit-Code setzen - wichtig für Cron-Mailbenachrichtigung
exit $ERRORS

Speichere das Script und mache es ausführbar:

# Script speichern
nano ~/paperless-backup/backup.sh

# Ausführbar machen
chmod +x ~/paperless-backup/backup.sh

Script testen

Bevor du den Cron-Job einrichtest, teste das Script einmal manuell:

~/paperless-backup/backup.sh

Prüfe danach das Logfile:

cat ~/paperless-backup/backup.log

Du solltest Einträge wie “Document Export erfolgreich” und “Datenbank-Dump erstellt” sehen. Falls Fehler auftreten, prüfe:

  • Stimmt der Pfad in PAPERLESS_DIR?
  • Läuft der PostgreSQL-Container? (docker compose ps)
  • Hat der Benutzer Docker-Rechte? (Gruppe docker)

Cron-Job einrichten

Öffne die Crontab und füge den Job hinzu:

crontab -e
# Paperless-NGX Backup taeglich um 3:00 Uhr
0 3 * * * /home/DEIN_USER/paperless-backup/backup.sh

Ersetze DEIN_USER durch deinen tatsächlichen Benutzernamen auf dem Pi.

💡 Tipp: 3:00 Uhr nachts ist ein guter Zeitpunkt - da werden in der Regel keine neuen Dokumente gescannt. Die Paperless-NGX Dokumentation empfiehlt, Backups zu erstellen, wenn gerade keine Dokumente verarbeitet werden.

Backup-Job überwachen

Ein Cron-Job, der still fehlschlägt, fällt erst auf, wenn du das Backup brauchst - also im schlimmsten Moment. Das Backup-Script oben zählt deshalb Fehler mit und setzt am Ende einen passenden Exit-Code. Darauf kannst du auf mehreren Wegen aufbauen:

Stufe 1: Logfile prüfen (schon eingebaut)

Das Script schreibt jeden Schritt ins Logfile. Am Ende steht Fehler: 0 oder die Anzahl der fehlgeschlagenen Schritte. Für den Anfang reicht ein gelegentlicher Blick:

tail -5 ~/paperless-backup/backup.log

Stufe 2: E-Mail bei Fehler (Cron-Bordmittel)

Cron kann die Ausgabe eines Jobs automatisch per Mail verschicken. Trage in deiner Crontab eine MAILTO-Adresse ein - dann bekommst du eine E-Mail, sobald das Script Fehlerausgaben produziert:

MAILTO="deine@email.de"
0 3 * * * /home/DEIN_USER/paperless-backup/backup.sh

Voraussetzung: Ein lokaler Maildienst wie msmtp oder ssmtp muss auf dem Pi eingerichtet sein.

Stufe 3: Cron-Monitoring mit CronWolf

Noch zuverlässiger: Das Script meldet am Ende aktiv success oder fail an einen Monitoring-Dienst. CronWolf ist mein eigener, DSGVO-konformer Cron-Monitoring-Service - gehostet in Deutschland, ohne Tracking. Du erstellst dort einen Check, der einen Ping in einem bestimmten Intervall erwartet. Bleibt der Ping aus - z.B. weil der Pi offline ist oder der Cron-Daemon nicht läuft - bekommst du eine Benachrichtigung per E-Mail, Slack, Discord oder Webhook. Das ist der entscheidende Vorteil gegenüber MAILTO: CronWolf erkennt auch, wenn ein Job gar nicht erst startet.

So richtest du es ein:

  1. Registriere dich kostenlos auf cronwolf.de
  2. Erstelle einen neuen Check mit Typ Cron und trage den Cron-Ausdruck ein, der zu deinem Crontab passt - z.B. 0 3 * * * für täglich um 3:00 Uhr. CronWolf erwartet den Ping dann genau zu dieser Zeit. Die Grace Period (z.B. 30 Minuten) gibt dem Backup-Script etwas Spielraum, falls es mal länger braucht.
  3. Kopiere die UUID aus den Ping-URLs im Dashboard
  4. Trage die UUID im Backup-Script unter CRONWOLF_UUID ein

CronWolf stellt dir drei Ping-Endpunkte bereit:

URLBedeutung
https://cronwolf.de/api/v2/ping/UUID/Erfolg (Success)
https://cronwolf.de/api/v2/ping/UUID/startJob gestartet (misst Laufzeit)
https://cronwolf.de/api/v2/ping/UUID/failJob fehlgeschlagen

Das Backup-Script nutzt alle drei: /start am Anfang, dann je nach Ergebnis die Basis-URL (Erfolg) oder /fail. So siehst du im Dashboard nicht nur ob das Backup lief, sondern auch wie lange es gedauert hat. Mehr Details zur Einrichtung findest du im Artikel CronWolf - Cron-Job Monitoring einfach erklärt.

Restore - Backup wiederherstellen

Ein Backup ist nur so gut wie der getestete Restore. Hier sind die Schritte, um aus einem Export eine funktionierende Paperless-NGX Instanz wiederherzustellen.

Voraussetzung: Leere Installation

Der Document Importer funktioniert nur mit einer komplett leeren Paperless-NGX Installation - leere Datenbank und leere Verzeichnisse. Auf einem neuen Pi oder in einer frischen Docker-Umgebung ist das gegeben.

Schritt 1: Frische Paperless-NGX Instanz starten

Stelle deine gesicherte docker-compose.yml und .env auf dem Zielsystem bereit:

# Backup-Konfiguration kopieren
mkdir -p ~/paperless-ngx
cp docker-compose.yml.backup ~/paperless-ngx/docker-compose.yml
cp env.backup ~/paperless-ngx/.env

# Container starten
cd ~/paperless-ngx
docker compose up -d

Warte, bis alle Container laufen:

docker compose ps

Schritt 2: Document Importer ausführen

Kopiere den Export in das Export-Volume und starte den Import:

# Import starten
docker compose exec -T webserver document_importer ../export

Der Importer liest die manifest.json und stellt alle Dokumente mit ihren Metadaten wieder her - Tags, Korrespondenten, Dokumenttypen, Benutzerberechtigungen. Nur API-Tokens müssen danach neu generiert werden.

Schritt 3: Installation prüfen

Nach dem Import:

  1. Öffne die Web-GUI (http://PI-IP:PORT - den Port findest du in deiner docker-compose.yml)
  2. Melde dich mit deinen Zugangsdaten an
  3. Prüfe ob alle Dokumente vorhanden sind
  4. Kontrolliere Tags, Korrespondenten und Dokumenttypen
  5. Öffne stichprobenartig einzelne Dokumente - PDFs und OCR-Text

Alternative: Restore aus dem Datenbank-Dump

Falls du nur die Datenbank wiederherstellen musst (z.B. nach einem fehlgeschlagenen Update), kannst du den PostgreSQL-Dump direkt einspielen:

# Dump entpacken falls komprimiert
gunzip db_20260329_030000.sql.gz

# Datenbank wiederherstellen
cat db_20260329_030000.sql | docker compose exec -T db psql -U paperless paperless

# Datenbank-Cache invalidieren (Pflicht nach DB-Restore!)
docker compose exec -T webserver python3 manage.py invalidate_cachalot

Nach einem Datenbank-Restore muss der Befehl invalidate_cachalot ausgeführt werden. Paperless-NGX nutzt einen internen Read-Cache (django-cachalot) - ohne Invalidierung arbeitet die Anwendung mit veralteten Daten. Alternativ: Den Webserver-Container nach dem Restore neu starten (docker compose restart webserver), das leert den Cache ebenfalls.

⚠️ Wichtig: Das überschreibt die aktuelle Datenbank. Im Zweifel: Erst ein neues Backup erstellen, dann den alten Dump einspielen.

Backup auf ein externes Ziel kopieren

Ein Backup auf dem gleichen Raspberry Pi schützt nicht vor Hardware-Ausfall. Kopiere die Daten regelmäßig auf ein anderes Gerät.

Per rsync auf ein NAS

# Backup auf NAS kopieren (ohne --delete!)
rsync -avz ~/paperless-backup/ /mnt/nas/backups/paperless/

Falls dein NAS per Netzwerkfreigabe eingebunden ist, zeigt dir mein Artikel zum Mounten von NAS-Freigaben wie du das einrichtest.

Wenn du mehrere Backup-Versionen aufbewahren willst, ohne den Speicherplatz zu verdoppeln, nutze rsync mit --link-dest. Dabei werden unveränderte Dateien als Hardlinks angelegt - sie belegen keinen zusätzlichen Platz:

# Tagesbackup mit Hardlinks auf die letzte Version
HEUTE=$(date +%Y-%m-%d)
BACKUP_ZIEL="/mnt/nas/backups/paperless/$HEUTE"
LETZTES="/mnt/nas/backups/paperless/latest"

rsync -avz --link-dest="$LETZTES" ~/paperless-backup/ "$BACKUP_ZIEL"

# Symlink auf das neueste Backup aktualisieren
ln -snf "$BACKUP_ZIEL" "$LETZTES"

So hast du tägliche Snapshots, aber nur geänderte Dateien verbrauchen tatsächlich Speicher.

Per rsync über SSH

# Backup auf einen zweiten Server kopieren
rsync -avz -e "ssh -p 22" ~/paperless-backup/ user@DEIN_SERVER:/backups/paperless/

Du kannst den rsync-Befehl direkt ans Ende deines Backup-Scripts hängen - dann wird das Offsite-Backup gleich miterledigt.

Wenn du das Backup über SSH auf einen entfernten Server kopierst, nutze einen dedizierten SSH-Key ohne Passphrase für den Cron-Job. Der Key sollte auf dem Zielserver nur Schreibrechte auf das Backup-Verzeichnis haben. Mehr zu SSH-Keys findest du im Artikel SSH-Verbindung unter einem Namen speichern.

Du willst Docker Backups nicht selbst einrichten? Ich übernehme das für dich —schau dir meine Services an

Restore regelmäßig testen

Ein Backup ohne Restore-Test ist wie ein Fallschirm, den du nie gepackt hast. Solange du nicht weißt, ob der Import funktioniert, hast du kein Backup - du hast eine Kopie und eine Hoffnung.

Manueller Test (alle 3 Monate)

  1. Frische Paperless-NGX Instanz in einem separaten Docker-Stack starten
  2. Export und Datenbank-Dump einspielen (wie im Restore-Abschnitt oben)
  3. Prüfen: Dokumentenanzahl, Tags, Korrespondenten, stichprobenartig PDFs öffnen
  4. Ergebnis dokumentieren - wann getestet, welche Version, alles OK?

Automatischer Restore-Test per Script

Noch besser: Ein Script, das den Restore automatisch in einem isolierten Docker-Stack testet. Das lässt sich z.B. monatlich per Cron ausführen:

#!/bin/bash
# Automatischer Paperless-NGX Restore-Test
# Startet eine isolierte Instanz, importiert das Backup, prueft das Ergebnis

BACKUP_DIR="$HOME/paperless-backup"
TEST_DIR="/tmp/paperless-restore-test"
LOG_FILE="$BACKUP_DIR/restore-test.log"
RESTORE_OK=false

# CronWolf Monitoring für den Restore-Test (optional)
# Eigener Check, getrennt vom Backup-Check!
CRONWOLF_RESTORE_UUID=""

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

log "=== Restore-Test gestartet ==="

# Pruefen ob Export-Verzeichnis und manifest.json existieren
if [ ! -f "$BACKUP_DIR/export/manifest.json" ]; then
    log "FEHLER: $BACKUP_DIR/export/manifest.json nicht gefunden"
    exit 1
fi

# Test-Verzeichnis neu anlegen
rm -rf "$TEST_DIR"
mkdir -p "$TEST_DIR/export"

# Export in Testverzeichnis kopieren - der Test arbeitet nur mit dieser Kopie
log "Kopiere Export nach $TEST_DIR/export/..."
rsync -a "$BACKUP_DIR/export/" "$TEST_DIR/export/"

# Eigene Test-Compose generieren - ausschliesslich Named Volumes
# Keine Bind-Mounts auf Produktionspfade! Export kommt als Kopie unter ./export.
# down -v betrifft NUR Volumes mit Prefix paperless-restore-test_ - nie deine Produktion!
cat > "$TEST_DIR/docker-compose.yml" << COMPOSE
services:
  broker:
    image: docker.io/library/redis:7
    restart: "no"
    volumes:
      - redisdata:/data

  db:
    image: docker.io/library/postgres:16
    restart: "no"
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: paperless
      POSTGRES_USER: paperless
      POSTGRES_PASSWORD: paperless

  webserver:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    restart: "no"
    depends_on:
      - db
      - broker
    ports:
      - "8099:8000"
    volumes:
      - data:/usr/src/paperless/data
      - media:/usr/src/paperless/media
      - ./export:/usr/src/paperless/export
    environment:
      PAPERLESS_REDIS: redis://broker:6379
      PAPERLESS_DBHOST: db
      PAPERLESS_DBNAME: paperless
      PAPERLESS_DBUSER: paperless
      PAPERLESS_DBPASS: paperless
      PAPERLESS_SECRET_KEY: restore-test-ephemeral-key-not-for-production

volumes:
  data:
  media:
  pgdata:
  redisdata:
COMPOSE

# Alte Test-Volumes bereinigen und frischen Stack starten
cd "$TEST_DIR"
docker compose -p paperless-restore-test down -v --remove-orphans 2>/dev/null || true
docker compose -p paperless-restore-test up -d 2>> "$LOG_FILE"

# Warten bis Paperless bereit ist (Healthcheck im Image, ca. 30-40 Sekunden)
log "Warte auf Paperless-Initialisierung..."
READY=false
for i in $(seq 1 36); do
    WS_STATUS=$(docker inspect -f '{{.State.Status}}' paperless-restore-test-webserver-1 2>/dev/null || echo "missing")
    WS_HEALTH=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' paperless-restore-test-webserver-1 2>/dev/null || echo "missing")
    if [ "$WS_STATUS" = "exited" ] || [ "$WS_STATUS" = "missing" ]; then
        log "FEHLER: Webserver-Container laeuft nicht (Status: $WS_STATUS)"
        docker compose -p paperless-restore-test logs webserver >> "$LOG_FILE" 2>&1
        break
    fi
    if [ "$WS_HEALTH" = "healthy" ]; then
        READY=true
        break
    fi
    sleep 5
done

if [ "$READY" != true ]; then
    log "FEHLER: Webserver nicht bereit (Health: $WS_HEALTH)"
    docker compose -p paperless-restore-test logs webserver >> "$LOG_FILE" 2>&1
fi

# Document Import ausfuehren
if [ "$READY" = true ]; then
    log "Starte Document Import..."
    if docker compose -p paperless-restore-test exec -T webserver document_importer ../export --no-progress-bar 2>> "$LOG_FILE"; then
        log "Import erfolgreich"
        RESTORE_OK=true
    else
        log "FEHLER: Import fehlgeschlagen!"
        docker compose -p paperless-restore-test logs webserver >> "$LOG_FILE" 2>&1
    fi
fi

# Test-Stack herunterfahren und komplett aufraeumen
docker compose -p paperless-restore-test down -v 2>> "$LOG_FILE"
rm -rf "$TEST_DIR"

# CronWolf benachrichtigen (falls konfiguriert)
if [ -n "$CRONWOLF_RESTORE_UUID" ]; then
    if [ "$RESTORE_OK" = true ]; then
        curl -fsS --retry 3 "https://cronwolf.de/api/v2/ping/$CRONWOLF_RESTORE_UUID/" > /dev/null 2>&1
        log "CronWolf: Restore-Test success"
    else
        curl -fsS --retry 3 "https://cronwolf.de/api/v2/ping/$CRONWOLF_RESTORE_UUID/fail" > /dev/null 2>&1
        log "CronWolf: Restore-Test fail"
    fi
fi

log "=== Restore-Test abgeschlossen (OK: $RESTORE_OK) ==="

Das Script generiert eine eigene, minimale Docker-Compose-Konfiguration mit ausschließlich Named Volumes. Dadurch ist der Test-Stack von deiner Produktionsinstanz getrennt - egal ob du Named Volumes oder Bind-Mounts verwendest. Der Backup-Export wird vorher nach /tmp/paperless-restore-test/export/ kopiert; der Test importiert nur diese Kopie. Vor jedem Lauf werden nur die Test-Volumes mit Prefix paperless-restore-test_ bereinigt. Statt eines festen Sleeps wartet das Script auf den Docker-Healthcheck des Webservers (typisch 30-40 Sekunden), bevor der Import startet. Schlägt der Start fehl, landen die Webserver-Logs automatisch in restore-test.log. Nach dem Test wird alles wieder gelöscht - inkl. der Export-Kopie unter /tmp. Während des Laufs braucht /tmp kurz genug Platz für die Kopie.

💡 Hinweis: Das Script nutzt ghcr.io/paperless-ngx/paperless-ngx:latest als Image. Falls du eine bestimmte Version pinnst, passe die Zeile in der generierten Compose an. Für den Import-Test ist eine abweichende Version in der Regel kein Problem - Paperless gibt nur eine Warnung aus.

Wie beim Backup-Script ist auch hier die CronWolf-Überwachung eingebaut - als separater Check mit eigenem Token (CRONWOLF_RESTORE_UUID). Damit hast du zwei Checks in deinem Dashboard: Einen für das tägliche Backup, einen für den monatlichen Restore-Test. Falls du CronWolf nicht nutzt, lässt du den Token einfach leer - das Script funktioniert trotzdem und schreibt das Ergebnis ins Logfile.

In den Cron trägst du den Test z.B. am ersten Sonntag im Monat ein:

# Restore-Test am 1. Sonntag im Monat um 5:00 Uhr
0 5 1-7 * 0 /home/DEIN_USER/paperless-backup/restore-test.sh

Prüfe danach gelegentlich das Logfile unter ~/paperless-backup/restore-test.log. Solange dort “Import erfolgreich” steht, weißt du: Dein Backup funktioniert.

Falls du bereits mein Docker Backup Script nutzt, deckt das die Container und Volumes ab. Der Document Exporter ergänzt das um eine Paperless-spezifische Sicherungsebene mit validiertem Import.

Hardware für Docker-Backups und Datensicherung

Werbung

Backup & Storage Hardware

Produktbilder ausgeblendet. Beim Laden wird deine IP-Adresse an Amazon übermittelt.

BildProduktPreis
Produktdaten werden geladen...
Letzte Aktualisierung: - |Infos zu Affiliate Links | Bilder von der Amazon Product Advertising API

Alle Schritte im Überblick

Hier die Kurzfassung als Checkliste:

  • Backup-Verzeichnis auf externem Speicher anlegen
  • Document Exporter manuell testen
  • PostgreSQL-Dump manuell testen
  • Backup-Script erstellen und ausführbar machen
  • Cron-Job einrichten (täglich, z.B. 3:00 Uhr)
  • Offsite-Kopie per rsync einrichten
  • Restore auf einer leeren Installation testen
  • Vierteljährlich: Restore-Test wiederholen

Wer seine Dokumente mit Paperless-NGX verwaltet, hat viel Arbeit in das Scannen, Taggen und Sortieren gesteckt. Ein automatisiertes Backup mit geprüftem Restore schützt diese Arbeit - und kostet nach der Einrichtung genau null Aufwand.

Weiterführende Artikel

FAQ - Frequently Asked Questions DarkWolfCave
DarkWolf hilft bei FAQs

Häufig gestellte Fragen

Was sichert der Document Exporter von Paperless-NGX?
Der Document Exporter erstellt eine Kopie aller Originaldokumente, Archiv-PDFs, Thumbnails und einer manifest.json mit sämtlichen Metadaten - Tags, Korrespondenten, Dokumenttypen und Benutzer.
Brauche ich einen separaten Datenbank-Dump neben dem Document Exporter?
Nicht zwingend, da der Exporter die Metadaten in der manifest.json speichert. Ein separater PostgreSQL-Dump ist aber als zusätzliche Absicherung sinnvoll und lässt sich schneller wiederherstellen als ein vollständiger Import.
Kann ich ein Backup im laufenden Betrieb erstellen?
Grundsätzlich ja. Die Paperless-NGX Dokumentation empfiehlt aber, sicherzustellen, dass gerade keine Dokumente verarbeitet werden. Am sichersten ist ein Backup zu einer Uhrzeit, in der kein neuer Scan-Input kommt.
Wie stelle ich ein Paperless-NGX Backup wieder her?
Mit dem Befehl document_importer auf einer leeren Paperless-NGX Installation. Der Importer liest die manifest.json und stellt Dokumente, Metadaten und Verzeichnisstruktur wieder her.
Funktioniert der Export von einer Paperless-NGX Version in einer anderen?
Nein. Laut offizieller Dokumentation muss der Import mit derselben Version erfolgen, mit der der Export erstellt wurde. Vor einem Update also immer zuerst ein Backup machen.
Wie viel Speicherplatz braucht ein Paperless-NGX Backup?
Rechne mit dem 1,5- bis 2-fachen deiner aktuellen Paperless-Daten. Der Export enthält Originale, Archiv-PDFs und Thumbnails. Mit Komprimierung (tar.gz) sparst du bei PDFs allerdings nur wenig Platz.

Kommentare

URLs werden automatisch verlinkt
Kommentare werden geladen...