Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🇩🇪 Deutsch | 🇬🇧 English


PABO – Paperless-Borg Backup Orchestrator

PABO (Paperless-Borg Backup Orchestrator) ist ein vollautomatisches Backup-System für Paperless-ngx – powered by BorgBackup und rclone. Unterstützt mehrere Cloud-Ziele gleichzeitig, verschlüsselte lokale Backups, automatische Integritätsprüfungen und wöchentliche Restore-Tests.

Hintergrund

Ich betreibe seit Jahren eine eigene Paperless-ngx-Instanz, die über die Zeit ordentlich gewachsen ist. Irgendwann war klar: ein simples cp reicht nicht mehr – ich wollte etwas Zuverlässiges, das automatisch läuft, verschlüsselt speichert und im Ernstfall wirklich wiederherstellbar ist.

Auf BorgBackup bin ich durch Vorträge aus dem CCC-Umfeld gestoßen. Die Kombination aus Deduplizierung, Verschlüsselung und Effizienz hat mich überzeugt. PABO ist das Ergebnis davon: ein Skript, das genau das tut was ich für meine Instanz brauche – nicht mehr, nicht weniger.


Inhaltsverzeichnis


Funktionsübersicht

Funktion Details
🔐 Verschlüsselung AES-256 via BorgBackup repokey
☁️ Multi-Cloud Beliebig viele rclone-Remotes gleichzeitig
🗄️ Datenbank PostgreSQL-Dump via pg_dump --clean --if-exists
📦 Deduplizierung Borg-interne Deduplizierung + LZ4-Kompression
🔁 Retention 14 täglich / 8 wöchentlich / 6 monatlich
✅ Integritätsprüfung Wöchentlicher borg check --verify-data
🧪 Restore-Test Wöchentlicher automatischer Dry-Run
📱 Benachrichtigungen Telegram bei Erfolg und Fehler
⏰ Automatisierung Systemd Timer (kein cron notwendig)

Voraussetzungen

System

  • Debian/Ubuntu-basiertes Linux (apt wird verwendet)
  • Docker + Docker Compose
  • Root-Zugriff

Software (wird automatisch installiert)

  • borgbackup ≥ 1.4
  • rclone
  • jq
  • curl
  • postgresql-client

Cloud-Speicher

Mindestens ein konfigurierter rclone-Remote. Falls noch keiner vorhanden ist, startet das Setup automatisch rclone config.

Unterstützte Anbieter (Auswahl): Google Drive, Dropbox, S3, Backblaze B2, OneDrive, SFTP, WebDAV – alle rclone-Remotes.


Installation

# Repository klonen
git clone https://github.com/ArnaudFeld/pabo.git /opt/pabo

# Symlink setzen
ln -s /opt/pabo/pabo.sh /usr/local/sbin/pabo.sh
chmod 755 /opt/pabo/pabo.sh

Updates

cd /opt/pabo && git pull

Ersteinrichtung

sudo pabo.sh
# → Menüpunkt 1) setup wählen

Der Setup-Assistent führt durch folgende Schritte:

  1. Abhängigkeiten installieren – borgbackup, rclone, jq, curl, postgresql-client
  2. rclone Remotes erkennen – oder rclone config starten falls keiner vorhanden
  3. Cloud-Ziele auswählen – ein oder mehrere Remotes + Zielpfad
  4. Docker-Container erkennen – Paperless + PostgreSQL werden automatisch erkannt
  5. Pfade bestätigen – Media, Data, Export, Compose-Datei
  6. Filesystem-Warnung – falls Borg-Repo auf demselben Laufwerk wie die Daten liegt
  7. Telegram konfigurieren – Bot-Token + Chat-ID
  8. rclone-Optionen – Bandbreitenlimit, parallele Transfers
  9. Borg-Excludes – Logs, NLTK-Daten, temporäre Dateien
  10. Borg-Repository initialisieren – AES-256 verschlüsselt
  11. Passphrase anzeigenmuss extern gesichert werden!
  12. Systemd Timer einrichten – automatischer Betrieb ab sofort

⚠️ Passphrase sichern

Nach dem Setup wird eine zufällige Passphrase generiert und in /root/.borg_passphrase gespeichert. Diese Datei ist der einzige Schlüssel zum Borg-Repository.

┌─────────────────────────────────────────┐
│  ⚠️  BORG PASSPHRASE – SICHER AUFBEWAHREN │
│  xK9mP2...                              │
│  Gespeichert: /root/.borg_passphrase    │
│  → extern sichern!                     │
└─────────────────────────────────────────┘

Empfehlung: Passphrase in einem Passwortmanager (Bitwarden, 1Password, KeePass) oder ausgedruckt an einem sicheren Ort aufbewahren.


Täglicher Betrieb

Nach dem Setup läuft alles automatisch über Systemd Timer:

Timer Zeitplan Aktion
paperless-backup-<remote>.timer Täglich 02:00 Uhr Backup + Upload
paperless-borg-check.timer Sonntags Borg-Integritätsprüfung
paperless-restore-test.timer Sonntags Automatischer Restore-Test

Bei mehreren Cloud-Zielen werden die Backup-Timer automatisch gestaffelt (02:00, 02:30, 03:00, …).

Timer-Status prüfen

systemctl list-timers | grep paperless

Logs einsehen

# Backup-Log
tail -50 /var/log/paperless-backup.log

# Borg Check-Log
tail -50 /var/log/paperless-borg-check.log

# Restore-Test-Log
tail -50 /var/log/paperless-restore-test.log

# Systemd Journal
journalctl -u paperless-backup-<remote>.service -n 50

Manuelle Aktionen

sudo pabo.sh
Menüpunkt Aktion
1) setup Ersteinrichtung oder Ziele ändern
2) restore Interaktiver Restore-Assistent
3) test Manuellen Backup/Check/Restore-Test starten
4) status Systemübersicht (Container, Timer, Archive, Logs)
5) config-check Konfiguration und Erreichbarkeit prüfen

Setup-Modi (bei bestehender Konfiguration)

Beim erneuten Aufruf von setup mit vorhandener /etc/paperless-backup.conf:

  • Modus 1 – Ziele ändern: Neue Cloud-Ziele einrichten, Borg und Passphrase bleiben unverändert
  • Modus 2 – Neu generieren: Scripts und Timer neu erstellen ohne andere Änderungen

Telegram-Token rotieren

# TELEGRAM_TOKEN in /etc/paperless-backup.conf manuell ersetzen, dann:
sudo pabo.sh  # → 1) setup → 2) Nur neu generieren

Restore

sudo pabo.sh
# → Menüpunkt 2) restore

Der Assistent bietet folgende Optionen:

Option Beschreibung
1) Voll-Restore Media + Data + docker-compose.yml + Datenbank
2) Nur Datenbank Nur PostgreSQL-Dump einspielen
3) Nur Media Nur Dokumentendateien wiederherstellen
4) Nur Data Nur Paperless-Data-Verzeichnis
5) Staging Restore in alternatives Verzeichnis (ohne laufendes System zu beeinflussen)

Manueller Restore bei totalem Systemverlust

# 1. Abhängigkeiten installieren
apt-get install -y borgbackup rclone jq curl postgresql-client

# 2. Passphrase wiederherstellen
echo "DEINE_PASSPHRASE" > /root/.borg_passphrase
chmod 600 /root/.borg_passphrase

# 3. Borg-Repo von Cloud herunterladen
rclone sync onedrive:/Paperless-Borg-Encrypted /backup/paperless-borg

# 4. Archive anzeigen
export BORG_PASSCOMMAND="cat /root/.borg_passphrase"
borg list /backup/paperless-borg

# 5. Restore starten
sudo pabo.sh  # → 2) restore

Hinweis zu schwerer Datenbank-Korruption: Falls psql beim Einspielen fehlschlägt, muss die Datenbank zuerst manuell geleert werden. Wichtig: Der DROP-Befehl muss über die postgres-Datenbank laufen, nicht über paperless (sonst: cannot drop the currently open database):

docker exec db psql -U paperless -d postgres -c "DROP DATABASE paperless;"
docker exec db psql -U paperless -d postgres -c "CREATE DATABASE paperless OWNER paperless;"

Eine Warnung wie collation version mismatch beim Verbinden ist harmlos und kann ignoriert werden – sie betrifft nur interne Sortierungsmetadaten und blockiert den Restore nicht.


Konfigurationsreferenz

Die Konfiguration liegt in /etc/paperless-backup.conf (chmod 600, nur root lesbar).

# PABO – Paperless Backup Konfiguration

PAPERLESS_CONTAINER="paperless-webserver"   # Docker Container Name
DB_CONTAINER="paperless-db"                 # PostgreSQL Container Name
COMPOSE_FILE="/home/paperless/docker-compose.yml"

DB_NAME="paperless"
DB_USER="paperless"

MEDIA_DIR="/data/paperless/media"
DATA_DIR="/data/paperless/data"
EXPORT_DIR="/data/paperless/export"
BORG_REPO="/backup/paperless-borg"          # Lokales Borg-Repository
BACKUP_TMP="/backup/paperless-tmp"          # Temporär für DB-Dump

# Bei Token-Rotation: setup → Modus 2 (neu generieren)
TELEGRAM_TOKEN="123456:ABC..."
TELEGRAM_CHAT_ID="987654321"

BACKUP_TARGETS=(
  onedrive:/Paperless-Borg-Encrypted        # Format: remote:/pfad
  gdrive:/Backups/Paperless
)

RCLONE_BWLIMIT="2M"                        # Leer = kein Limit, z.B. "2M", "500K"
RCLONE_TRANSFERS="4"
RCLONE_CHECKERS="8"

BORG_EXCLUDES=(
  "/data/paperless/data/log"
  "/data/paperless/data/nltk"
  "*.tmp"
  "*.swp"
  "*.lock"
)

ENABLE_DOCUMENT_EXPORTER="false"           # true = document_exporter vor Backup
EXPORTER_DEST="/usr/src/paperless/export"

Architektur

pabo.sh
│
├── /etc/paperless-backup.conf          ← Zentrale Konfiguration (chmod 600)
├── /root/.borg_passphrase              ← Borg-Passphrase (chmod 600)
│
├── /usr/local/lib/
│   └── paperless-backup-common.sh     ← Shared Library (run_backup, send_telegram, …)
│
├── /usr/local/bin/
│   ├── paperless-backup-<remote>.sh   ← Pro Cloud-Ziel ein Script
│   ├── paperless-borg-check.sh        ← Wöchentlicher Integritätscheck
│   └── paperless-restore-test.sh      ← Wöchentlicher Restore Dry-Run
│
└── /etc/systemd/system/
    ├── paperless-backup-<remote>.{service,timer}
    ├── paperless-borg-check.{service,timer}
    └── paperless-restore-test.{service,timer}

Backup-Ablauf pro Ziel

flock (Lock pro Remote)
  │
  ├── [optional] document_exporter
  ├── pg_dump → /backup/paperless-tmp/paperless-db.sql
  ├── borg create (Media + Data + DB-Dump + compose.yml)
  ├── borg prune (14d/8w/6m)
  ├── borg compact
  └── rclone sync → Cloud

Sicherheitshinweise

Aspekt Maßnahme
Verschlüsselung AES-256 repokey – Daten sind in der Cloud ohne Passphrase unlesbar
Config-Schutz /etc/paperless-backup.conf chmod 600, nur root lesbar
Passphrase Nur als Lesebefehl in der Umgebung (BORG_PASSCOMMAND), nie als Klartext
Telegram Bot-Token in Config – bei Kompromittierung über @BotFather rotieren + Modus 2
Locks Pro Remote ein eigener flock-Lock – verhindert parallele Ausführung
Passphrase-Verlust Backup ist dauerhaft verloren – extern sichern!

Fehlerbehandlung & Exit-Codes

Code Bedeutung
0 Erfolgreich
10 PostgreSQL-Dump fehlgeschlagen
11 Borg create/check fehlgeschlagen
12 rclone Upload fehlgeschlagen
13 Restore fehlgeschlagen
14 Restore-Test fehlgeschlagen

Bei jedem Fehler wird eine Telegram-Nachricht mit Exit-Code und betroffener Komponente gesendet.


Häufige Probleme

❌ /root/.borg_passphrase nicht gefunden

Die Passphrase-Datei fehlt. Manuell erstellen:

echo "DEINE_PASSPHRASE" > /root/.borg_passphrase
chmod 600 /root/.borg_passphrase

⚠️ Backup läuft bereits (Lock aktiv)

Ein anderer Backup-Prozess ist noch aktiv. Prüfen mit:

ps aux | grep paperless-backup
ls /var/lock/paperless-backup-*.lock

Borg-Repository nicht erreichbar

export BORG_PASSCOMMAND="cat /root/.borg_passphrase"
borg info /backup/paperless-borg

rclone-Remote fehlt

rclone listremotes
rclone config  # Remote neu einrichten
sudo pabo.sh  # → 1) setup → 1) Ziele ändern

Telegram-Nachrichten kommen nicht an

# Token und Chat-ID testen:
curl -s "https://api.telegram.org/bot<TOKEN>/getMe"
curl -s "https://api.telegram.org/bot<TOKEN>/sendMessage" \
  -d "chat_id=<CHAT_ID>&text=Test"

Borg Check schlägt fehl

export BORG_PASSCOMMAND="cat /root/.borg_passphrase"
borg check --repair /backup/paperless-borg
# Wenn nicht reparierbar: Restore vom letzten funktionierenden Cloud-Backup

ERROR: cannot drop the currently open database

Der DROP DATABASE-Befehl darf nicht über die zu löschende Datenbank selbst laufen. Stattdessen über postgres verbinden:

docker exec db psql -U paperless -d postgres -c "DROP DATABASE paperless;"
docker exec db psql -U paperless -d postgres -c "CREATE DATABASE paperless OWNER paperless;"

WARNING: collation version mismatch

Diese Warnung erscheint wenn die PostgreSQL-Collation-Version des Containers nicht mit der des Betriebssystems übereinstimmt. Sie ist harmlos und blockiert weder Backup noch Restore. Optional beheben mit:

docker exec db psql -U paperless -d postgres -c "ALTER DATABASE paperless REFRESH COLLATION VERSION;"
docker exec db psql -U paperless -d postgres -c "ALTER DATABASE template1 REFRESH COLLATION VERSION;"

About

Automatische, verschlüsselte Backups für Paperless-ngx | Automated, encrypted backups for Paperless-ngx

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages