Du betrachtest gerade Paperless GoBD konforme Datensicherung

Paperless GoBD konforme Datensicherung

Das heikle Thema Paperless GoBd konforme Datensicherung. Hier eine einfache Lösung mit allem was du wissen musst.

Wer seine privaten oder geschäftlichen Dokumente digitalisiert, stößt unweigerlich auf das mächtige Open-Source-Tool Paperless-ngx. Doch mit der wachsenden Menge an sensiblen Daten, Rechnungen und steuerrelevanten Belegen steigt auch die Verantwortung. Spätestens, wenn man sich im europäischen Raum bewegt, schwebt das Damoklesschwert der GoBD (Grundsätze zur ordnungsmäßigen Führung und Aufbewahrung von Büchern, Aufzeichnungen und Unterlagen in elektronischer Form) über der eigenen Festplatte.

In diesem Leitfaden räumen wir mit gefährlichem Halbwissen auf, erklären den fundamentalen Unterschied zwischen einem Export und einem echten Backup und zeigen Schritt für Schritt, wie eine hocheffiziente, generationsbasierte Datensicherung mittels rsync und Hardlinks unter Windows (WSL) aufgesetzt wird.

Video: Paperless GoBD konforme Datensicherung

Sprache: 🇩🇪|🇬🇧
☝️ Benutze YouTube Untertitel für alle Sprachen, KI Audio Englisch

Der große Irrtum: Exportieren ist kein Backup!

Häufig nutzen Anwender das in Paperless-ngx integrierte Werkzeug document_exporter und wiegen sich in der Sicherheit, damit ein vollwertiges Backup zu besitzen. Das ist ein Trugschluss, der im Ernstfall oder bei einer Betriebsprüfung böse enden kann. Beide Werkzeuge erfüllen völlig unterschiedliche Zwecke:

Das „Exportieren“ (via document_exporter)

Der exporter zieht alle vorhandenen Dokumente aus der Paperless-Struktur ab und legt sie gemeinsam mit einer Datei namens manifest.json in einem klassischen Ordner ab.

  • Der Sinn des Exporters: Er dient der Datenportabilität und der Unabhängigkeit. Sollte das Projekt Paperless-ngx jemals eingestellt werden, liegen die PDFs sauber benannt auf der Festplatte. Zudem ermöglicht der Export den Umzug auf ein komplett anderes System via document_importer.
  • Das GoBD-Problem: Der Export für sich allein ist nicht revisionssicher. Die exportierten PDFs und die JSON-Datei liegen unverschlüsselt auf dem Betriebssystem. Sie können nachträglich unbemerkt manipuliert, gelöscht oder verändert werden – für das Finanzamt ist dies ein absolutes K.-o.-Kriterium.

Die Pluspunkte eines echten System-Backups

Ein echtes technischen Backup hingegen sichert die gesamte Infrastruktur „von außen“. Es kombiniert einen konsistenten Datenbank-Dump (z. B. PostgreSQL) mit den exakten Systemverzeichnissen der Docker-Container.

  • Maximale Datensicherheit: Es erfasst alle Metadaten, Benutzerkonten, Protokolle und originalen Zeitstempel unveränderbar direkt aus der Datenbank.
  • Disaster Recovery im Handumdrehen: Raucht die Festplatte ab, werden einfach die Docker-Verzeichnisse zurückkopiert und die Container neu gestartet. Das System läuft exakt an dem Punkt weiter, an dem es aufgehört hat.
  • Voraussetzung für GoBD: Kombiniert man dieses technische Backup (oder die Jahres-Exporte) mit einem unveränderbaren Speichermedium (WORM/Snapshot-Lock), erzielt man eine revisionssichere Langzeitarchivierung, die auch der Betriebsprüfung standhält.

Teil 1: Anleitung für Umsteiger (Vom alten tar.gz/ZIP-Backup auf das neue rsync-Hardlink-System)

Wenn bisher eine klassische Windows-Batchdatei (.bat) genutzt wurde, die Backups als .tar.gz oder .zip komprimiert hat, blockiert diese alte Struktur das neue, hocheffiziente Hardlink-Verfahren. Bei Hardlinks verbrauchen unveränderte Dateien bei der täglichen Sicherung exakt 0 Byte zusätzlichen Speicherplatz, verhalten sich aber in jedem Tagesordner wie ein eigenständiges Vollbackup.

Hier ist der Migrationspfad, um das System sauber umzustellen:

Schritt 1: rsync in der WSL installieren

Da das neue Sicherungsskript nativ unter Linux läuft, müssen wir sicherstellen, dass das Synchronisations-Werkzeug rsync in der Windows-Subsystem für Linux (WSL) Umgebung vorhanden ist.

  1. Öffne das WSL-Terminal (z. B. Ubuntu).
  2. Aktualisiere die Paketquellen und installiere rsync mit folgendem Befehl:Bashsudo apt-get update && sudo apt-get install -y rsync

Schritt 2: Den alten Backup-Ordner aufräumen

Das neue rsync-Skript sucht im Zielverzeichnis nach dem jeweils letzten Backup, um darauf die Hardlinks aufzubauen. Befinden sich dort noch alte .tar.gz-Archive aus der Windows-Welt, bricht rsync mit der Fehlermeldung --link-dest arg is not a dir ab.

  1. Navigiere auf dem Windows-Laufwerk zu dem bisherigen Backup-Pfad (z. B. D:\Backups\Paperless\media).
  2. Erstelle dort einen temporären Unterordner (z. B. alte_backups) und verschiebe alle alten komprimierten Archivdateien dorthin. Die Unterordner im Backup-Verzeichnis müssen komplett frei von alten Archiv-Dateien sein.

Schritt 3: Das neue Shell-Skript anlegen

Erstelle im Paperless-Stammverzeichnis (z. B. /mnt/d/DockerServer/paperless/) eine neue Datei namens backup.sh. Verwende dafür einen Linux-kompatiblen Editor oder erstelle sie direkt im Terminal via nano backup.sh. Kopiere den folgenden Code hinein:

Bash | Sicherung mit eml2pdf Add-on, beinhaltet das eml-import Verzeichnis

#!/bin/bash

# ==========================================
# KONFIGURATION & AUFBEWAHRUNGSZEIT
# ==========================================
KEEP_VERSIONS=3
BACKUP_DIR="/mnt/d/Backups/Paperless"

DOCKER_STAMM_DIR="/mnt/d/DockerServer/paperless"
MEDIA_DIR="/mnt/d/DockerServer/paperless/media"
EML_IMPORT_DIR="/mnt/d/DockerServer/paperless/eml-import"
EXPORT_DIR="/mnt/d/DockerServer/paperless/export"
DATA_DIR="/mnt/d/DockerServer/paperless/data"

DATE=$(date +%Y-%m-%d)

echo "Starte Paperless-ngx Backup-Prozess für den $DATE..."

# Zielordner strukturieren
mkdir -p "$BACKUP_DIR/db"
mkdir -p "$BACKUP_DIR/stamm/$DATE"
mkdir -p "$BACKUP_DIR/media/$DATE"
mkdir -p "$BACKUP_DIR/eml-import/$DATE"
mkdir -p "$BACKUP_DIR/export/$DATE"
mkdir -p "$BACKUP_DIR/data/$DATE"

# 1. Datenbank-Sicherung (Garantiert UTF-8 & Umlaut-sicher)
echo "[$DATE] Starte PostgreSQL-Backup..."
docker exec paperless-dbpg pg_dumpall -U paperless --encoding=UTF8 > "$BACKUP_DIR/db/paperless_db_$DATE.sql"

# rsync Hilfsfunktion für inkrementelle Hardlinks
run_rsync_backup() {
    local src="$1"
    local dest_subfolder="$2"
    local extra_args="$3"

    local last_backup=$(find "$BACKUP_DIR/$dest_subfolder" -mindepth 1 -maxdepth 1 -type d ! -name "$DATE" | sort | tail -n 1)

    if [ -n "$last_backup" ]; then
        echo "[$DATE] Synchronisiere $dest_subfolder (Nutze Hardlinks auf: $(basename "$last_backup"))..."
        rsync -av --delete --link-dest="$last_backup" $extra_args "$src/" "$BACKUP_DIR/$dest_subfolder/$DATE/"
    else
        echo "[$DATE] Synchronisiere $dest_subfolder (Erstes Vollbackup)..."
        rsync -av --delete $extra_args "$src/" "$BACKUP_DIR/$dest_subfolder/$DATE/"
    fi
}

# 2. Synchronisation der Verzeichnisse
# Beim Stammordner schließen wir Medien, Exporte sowie die aktiven Docker-Datenbank-Rohdaten aus
run_rsync_backup "$DOCKER_STAMM_DIR" "stamm" "--exclude=/media --exclude=/eml-import --exclude=/export --exclude=/data --exclude=/pgdata --exclude=/redisdata"
run_rsync_backup "$MEDIA_DIR" "media" ""
run_rsync_backup "$EML_IMPORT_DIR" "eml-import" ""
run_rsync_backup "$EXPORT_DIR" "export" ""
run_rsync_backup "$DATA_DIR" "data" ""

# 3. Bereinigung alter Stände
echo "[$DATE] Entferne alte Backups (Behalte die letzten $KEEP_VERSIONS Versionen)..."
ls -1tr "$BACKUP_DIR/db"/paperless_db_*.sql 2>/dev/null | head -n -$KEEP_VERSIONS | xargs -r rm

cleanup_old_folders() {
    local folder="$1"
    find "$BACKUP_DIR/$folder" -mindepth 1 -maxdepth 1 -type d | sort | head -n -$KEEP_VERSIONS | xargs -r rm -rf
}
cleanup_old_folders "stamm"
cleanup_old_folders "media"
cleanup_old_folders "eml-import"
cleanup_old_folders "export"
cleanup_old_folders "data"

echo "Backup erfolgreich abgeschlossen: $DATE."

Bash | Sicherung ohne eml2pdf Add-on, beinhaltet das eml-import Verzeichnis nicht. Hier kannst Du sehen wie du Verzeichnisse einschließt oder ausschließt.

#!/bin/bash

# ==========================================
# KONFIGURATION & AUFBEWAHRUNGSZEIT
# ==========================================
KEEP_VERSIONS=3
BACKUP_DIR="/mnt/d/Backups/Paperless"

DOCKER_STAMM_DIR="/mnt/d/DockerServer/paperless"
MEDIA_DIR="/mnt/d/DockerServer/paperless/media"
EXPORT_DIR="/mnt/d/DockerServer/paperless/export"
DATA_DIR="/mnt/d/DockerServer/paperless/data"

# ==========================================
# DATUMSBESTIMMUNG
# ==========================================
DATE=$(date +%Y-%m-%d)

echo "Starte Paperless-ngx Backup-Prozess für den $DATE..."

# Sicherstellen, dass die Zielordner existieren
mkdir -p "$BACKUP_DIR/db"
mkdir -p "$BACKUP_DIR/stamm/$DATE"
mkdir -p "$BACKUP_DIR/media/$DATE"
mkdir -p "$BACKUP_DIR/export/$DATE"
mkdir -p "$BACKUP_DIR/data/$DATE"

# ==========================================
# 1. POSTGRESQL-DATABASE BACKUP
# ==========================================
echo "[$DATE] Starte PostgreSQL-Backup..."
docker exec paperless-dbpg pg_dumpall -U paperless --encoding=UTF8 > "$BACKUP_DIR/db/paperless_db_$DATE.sql"

# ==========================================
# rsync HILFS-FUNKTION FÜR HARDLINKS
# ==========================================
run_rsync_backup() {
    local src="$1"
    local dest_subfolder="$2"
    local extra_args="$3"

    local last_backup=$(find "$BACKUP_DIR/$dest_subfolder" -mindepth 1 -maxdepth 1 -type d ! -name "$DATE" | sort | tail -n 1)

    if [ -n "$last_backup" ]; then
        echo "[$DATE] Synchronisiere $dest_subfolder (Nutze Hardlinks auf: $(basename "$last_backup"))..."
        rsync -av --delete --link-dest="$last_backup" $extra_args "$src/" "$BACKUP_DIR/$dest_subfolder/$DATE/"
    else
        echo "[$DATE] Synchronisiere $dest_subfolder (Erstes Vollbackup)..."
        rsync -av --delete $extra_args "$src/" "$BACKUP_DIR/$dest_subfolder/$DATE/"
    fi
}

# ==========================================
# RSYNC DURCHLÄUFE TRIGGERN
# ==========================================
run_rsync_backup "$DOCKER_STAMM_DIR" "stamm" "--exclude=/media --exclude=/eml-import --exclude=/export --exclude=/data --exclude=/pgdata --exclude=/redisdata"
run_rsync_backup "$MEDIA_DIR" "media" ""
run_rsync_backup "$EXPORT_DIR" "export" ""
run_rsync_backup "$DATA_DIR" "data" ""

# ==========================================
# BEREINIGUNG
# ==========================================
echo "[$DATE] Entferne alte Backups (Behalte die letzten $KEEP_VERSIONS Versionen)..."

ls -1tr "$BACKUP_DIR/db"/paperless_db_*.sql 2>/dev/null | head -n -$KEEP_VERSIONS | xargs -r rm

cleanup_old_folders() {
    local folder="$1"
    find "$BACKUP_DIR/$folder" -mindepth 1 -maxdepth 1 -type d | sort | head -n -$KEEP_VERSIONS | xargs -r rm -rf
}

cleanup_old_folders "stamm"
cleanup_old_folders "media"
cleanup_old_folders "export"
cleanup_old_folders "data"

echo "Backup erfolgreich abgeschlossen: $DATE."

Schritt 4: Rechte vergeben und Erststart

Damit das Skript ausgeführt werden darf, muss ihm unter Linux das Ausführungsrecht zugewiesen werden. Da Docker-Verzeichnisse wie pgdata oft Root-Rechte besitzen, führen wir das Skript zwingend mit sudo aus:

Bash

chmod +x /mnt/d/DockerServer/paperless/backup.sh
sudo ./backup.sh

Beim ersten Durchlauf wird nun ein vollständiges Abbild aller Ordner erzeugt. Ab dem zweiten Durchlauf greift die Hardlink-Logik vollautomatisch.

Teil 2: Vereinfachte Anleitung für die komplette Neueinrichtung

Wer frisch startet und von Anfang an alles richtig aufsetzen möchte, folgt dieser gestrafften Anleitung.

Schritt 1: rsync in WSL installieren

Öffne das WSL-Terminal und vergewissere dich mit zwei kurzen Befehlen, dass das System auf dem neuesten Stand und rsync einsatzbereit ist:

Bash

sudo apt-get update
sudo apt-get install -y rsync

Schritt 2: Skriptdatei erstellen und anpassen

Erstelle den Backup-Ablauf direkt an Ort und Stelle:

Bash

nano /mnt/d/DockerServer/paperless/backup.sh

Füge den Skript-Code aus Teil 1 ein. Überprüfe gegebenenfalls die Pfade in der Konfigurations-Sektion ganz oben, falls deine Ordnerstruktur auf einem anderen Laufwerk liegt (z. B. /mnt/c/... statt /mnt/d/...). Speichere mit Strg + O und beende mit Strg + X.

Mache das Skript ausführbar:

Bash

chmod +x /mnt/d/DockerServer/paperless/backup.sh

Schritt 3: Automatisierung einrichten (Cronjob)

Damit die Datensicherung verlässlich ohne manuelles Zutun im Hintergrund abläuft, übergeben wir den Job an den Linux-Systemdienst cron. Da das Skript tiefgreifende Leserechte benötigt, editieren wir den Crontab des Root-Benutzers:

Bash

sudo crontab -e

Füge ganz am Ende der Datei die folgende Zeile hinzu, um das Backup beispielsweise jeden Montag und Freitag um 21:30 Uhr vollautomatisch auszuführen:

Code-Snippet

30 21 * * 1,5 /mnt/d/DockerServer/paperless/backup.sh > /mnt/d/Backups/Paperless/backup_cron.log 2>&1

Hinweis: Die Endung > ... backup_cron.log 2>&1 sorgt dafür, dass sämtliche Ausgaben und Fehlermeldungen bei der automatischen Ausführung in eine Logdatei geschrieben werden. So kann jederzeit überprüft werden, ob die Sicherung reibungslos verlaufen ist.

Fazit zur GoBD-Strategie

Mit dieser Einrichtung ist die technische Seite perfekt abgedeckt. Für eine lückenlose GoBD-Konformität im europäischen Raum empfiehlt sich nun folgende Aufteilung: Das hier eingerichtete backup.sh-Skript sichert das System fortlaufend gegen plötzliche Datenverluste ab. Einmal im Jahr wird zusätzlich der document_exporter angeworfen, um einen sauberen Jahresabschluss-Ordner zu generieren. Wird dieser Jahresordner anschließend auf einem schreibgeschützten, unveränderbaren Medium (wie einem NAS mit WORM-Ordnern oder einer unveränderbaren Cloud-Ablage) für 10 Jahre archiviert, schläft es sich auch vor der nächsten Betriebsprüfung absolut beruhigt.


Spenden Bild

Link zur Unterstützung / Spende für den Kanal
Wenn meine Beiträge hilfreich sind oder dir geholfen haben, würde ich mich über eine Unterstützung sehr freuen 🙏

PayPal Link
Überweisung, Bitcoin und Lightning


#Paperlessngx #GoBD #Datensicherung #Docker #Rsync #Backup #Selfhosting

Schreibe einen Kommentar