- #4: LXC/Container Drop-in (lxc-compat.conf) deaktiviert systemd-Hardening; Installer erkennt Container automatisch und bietet Drop-in an - #5: WorkingDirectory=/opt/pdf-ocr-hotfolder in Template-Unit ergänzt - #6: Installer bietet auf Debian 12 bei betroffenen GS-Versionen automatisch bookworm-backports Upgrade an (statt nur Warnung) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PDF OCR Hotfolder
Verwandelt eingehende gescannte PDFs automatisch in durchsuchbare PDFs (PDF/A optional) per OCR. Hauptanwendung: Kunden-Scanner schiebt PDF in einen Ordner — Sekunden später liegt die OCR-Version im Ausgang oder wird in Nextcloud / per SFTP weitergeleitet.
Features
- 🔍 OCR via ocrmypdf + Tesseract (Library-Call, kein Subprozess-Overhead)
- 📂 Hotfolder via watchdog — reagiert auf
created,moved,closedEvents - 🧠 Stabilitäts-Erkennung: wartet bis Scanner fertig geschrieben hat
- 🔁 Parallelverarbeitung mehrerer PDFs (ThreadPool, konfigurierbar)
- ✅ PDF/A-Output (1, 2 oder 3) optional
- 🛡️ veraPDF-Validierung optional
- ☁️ Upload-Ziele: lokaler Ordner, Nextcloud (WebDAV via Python), SFTP
- 📧 E-Mail-Notify (immer / nur Fehler / nie)
- 🔐 Service-User-Support für lokale und AD-User mit lokaler UID (SSSD/Winbind)
- ⚙️ Saubere systemd-Integration mit auto-Restart
Schnellstart
git clone https://gitea.sonith.de/sonith_ug/pdf-ocr-hotfolder.git
cd pdf-ocr-hotfolder
sudo ./install.sh
Der Installer:
- Installiert einmalig Code + venv + systemd-Template-Unit
- Fragt nach Instanz-Name, Basis-Pfad, Service-User
- Legt so viele Hotfolder-Instanzen an, wie du willst (
Weitere Instanz anlegen? [j/N])
Bei jedem erneuten Aufruf erkennt der Installer bestehende Instanzen und fragt nur nach neuen.
Test:
cp irgendein-scan.pdf /var/lib/pdf-ocr-hotfolder/<instanz>/incoming/
journalctl -u pdf-ocr-hotfolder@<instanz> -f
Nach wenigen Sekunden liegt das OCR-PDF im outgoing/-Ordner der Instanz.
Multi-Instanz-Betrieb
Das Tool arbeitet komplett instanzbasiert über eine systemd Template-Unit pdf-ocr-hotfolder@<name>.service. Jede Instanz hat:
- eigene Config-Datei:
/etc/pdf-ocr-hotfolder/<name>.toml - eigene Datenverzeichnisse:
/var/lib/pdf-ocr-hotfolder/<name>/{incoming,working,outgoing,error}/ - eigene systemd-Unit:
pdf-ocr-hotfolder@<name>.service - optional eigenen Service-User (via Drop-in
/etc/systemd/system/pdf-ocr-hotfolder@<name>.service.d/user.conf)
Beispiel für 3 Hotfolder:
sudo ./install.sh
# → legt z.B. kunde-a, kunde-b, buchhaltung an
systemctl status 'pdf-ocr-hotfolder@*'
journalctl -u pdf-ocr-hotfolder@kunde-a -f
Manuell eine weitere Instanz anlegen geht auch — einfach install.sh erneut starten, er fragt wieder nach.
Verzeichnisse
| Pfad | Zweck |
|---|---|
/opt/pdf-ocr-hotfolder/ |
Code + venv (für alle Instanzen gemeinsam) |
/etc/pdf-ocr-hotfolder/<instanz>.toml |
Config pro Instanz |
/etc/systemd/system/pdf-ocr-hotfolder@.service |
systemd Template-Unit |
/var/lib/pdf-ocr-hotfolder/<instanz>/incoming |
Eingang (Scanner schreibt hier rein) |
/var/lib/pdf-ocr-hotfolder/<instanz>/working |
Arbeitsverzeichnis während OCR |
/var/lib/pdf-ocr-hotfolder/<instanz>/outgoing |
Ausgang (fertige PDFs) |
/var/lib/pdf-ocr-hotfolder/<instanz>/error |
Fehlgeschlagene PDFs |
/var/log/pdf-ocr-hotfolder/ |
Logs (zusätzlich zu journald) |
/var/backups/pdf-ocr-hotfolder/ |
Update-Backups |
Konfiguration
Vollständiges Beispiel: config.example.toml. Wichtigste Sektionen:
[ocr]
languages = "deu+eng" # Tesseract-Sprachen
jobs = 4 # Threads pro PDF
skip_text = true # bereits OCR-haltige Seiten überspringen
pdfa_level = "2" # "1", "2", "3" oder "" für reines PDF
deskew = true
max_workers = 2 # parallele PDFs
timeout = 1800
[output]
# Dateiname im outgoing/:
# "prefix" → OCR_scan.pdf
# "suffix" → scan_OCR.pdf (vor der Extension)
# "none" → scan.pdf (unverändert)
name_mode = "prefix"
name_tag = "OCR_"
# Nach erfolgreichem OCR mit dem Original:
# "delete" → löschen
# "archive" → in archive_dir verschieben
original_on_success = "delete"
archive_dir = "" # absoluter Pfad, Pflicht bei "archive"
[upload.nextcloud]
enabled = true
url = "https://cloud.example.com"
username = "scanuser"
password = "app-password"
remote_path = "Scans/Inbox"
[upload.sftp]
enabled = true
host = "sftp.example.com"
username = "scanuser"
key_file = "/etc/pdf-ocr-hotfolder/sftp_key"
remote_path = "/uploads"
[notify.email]
enabled = true
smtp_host = "smtp.example.com"
smtp_port = 587
smtp_user = "alerts@example.com"
smtp_password = "secret"
from_addr = "PDF OCR <alerts@example.com>"
to_addrs = ["admin@example.com"]
on = "errors" # always | errors | never
Service-Verwaltung
# Eine bestimmte Instanz
sudo systemctl status pdf-ocr-hotfolder@kunde-a
sudo systemctl restart pdf-ocr-hotfolder@kunde-a
journalctl -u pdf-ocr-hotfolder@kunde-a -f
# Alle Instanzen
sudo systemctl status 'pdf-ocr-hotfolder@*'
sudo systemctl restart 'pdf-ocr-hotfolder@*'
Update
cd /pfad/zum/repo
git pull
sudo ./update.sh
update.sh:
- Stoppt alle laufenden Instanzen
- Sichert den alten Code nach
/var/backups/pdf-ocr-hotfolder/ - Aktualisiert Code + venv + systemd-Template-Unit in
/opt/pdf-ocr-hotfolder/ - Startet alle zuvor laufenden Instanzen neu
Config-Dateien unter /etc/pdf-ocr-hotfolder/ werden nie überschrieben.
Das Repo muss bestehen bleiben — update.sh kopiert daraus.
Manueller Lauf (One-Shot)
Bestehende PDFs einer Instanz einmalig verarbeiten und beenden:
sudo -u pdfocr /opt/pdf-ocr-hotfolder/venv/bin/python -m pdf_ocr_hotfolder \
--config /etc/pdf-ocr-hotfolder/kunde-a.toml --once
Troubleshooting
Tesseract findet die Sprache nicht
sudo apt install tesseract-ocr-deu tesseract-ocr-eng
"PriorOcrFoundError"
ocrmypdf erkennt bereits vorhandenen OCR-Text. skip_text = true in der Config setzen.
Berechtigungsprobleme bei AD-User
Service-User braucht rw auf alle vier Verzeichnisse unter /var/lib/pdf-ocr-hotfolder/. Bei AD-User mit lokaler UID:
sudo chown -R DOMAIN\\scanuser:DOMAIN\\scangroup /var/lib/pdf-ocr-hotfolder
LXC/Container: Error 226/NAMESPACE
In LXC-Containern schlagen systemd-Hardening-Optionen fehl. Der Installer erkennt Container automatisch und bietet ein Drop-in an. Manuell:
sudo mkdir -p /etc/systemd/system/pdf-ocr-hotfolder@.service.d/
sudo cp /opt/pdf-ocr-hotfolder/systemd/lxc-compat.conf \
/etc/systemd/system/pdf-ocr-hotfolder@.service.d/
sudo systemctl daemon-reload
sudo systemctl restart 'pdf-ocr-hotfolder@*'
Ghostscript PDF/A-Bug auf Debian 12
GS 10.00.0–10.02.0 (Debian 12 Default) zerstört OCR bei pdfa_level + skip_text=true. Der Installer bietet automatisch bookworm-backports an. Manuell:
echo 'deb http://deb.debian.org/debian bookworm-backports main' | \
sudo tee /etc/apt/sources.list.d/bookworm-backports.list
sudo apt update && sudo apt install -t bookworm-backports ghostscript
veraPDF-Validierung schlägt immer fehl
veraPDF binary prüfen ([verapdf].binary). Wenn nicht zwingend gebraucht: enabled = false.
Architektur
┌──────────┐ watchdog ┌──────────────┐ ocrmypdf ┌──────────┐
│ Scanner │ ──────────────▶ │ incoming/ │ ─────────────▶ │ working/ │
└──────────┘ PDF-Datei └──────────────┘ (Library) └────┬─────┘
│
optional veraPDF
│
▼
┌──────────────┐
│ outgoing/ │
└──────┬───────┘
│
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Nextcloud │ │ SFTP │ │ E-Mail │
│ (WebDAV) │ │ (paramiko) │ │ Notify │
└────────────┘ └────────────┘ └────────────┘
Lizenz
MIT — © Sonith UG
Version: 0.3.1 Repo: https://gitea.sonith.de/sonith_ug/pdf-ocr-hotfolder