Files
pdf-ocr-hotfolder/docs/OS-UPGRADE.md
T
techadmin 465ff8873f fix: Ghostscript-Angebot log nicht mehr, fehlende Voraussetzungen dokumentiert (v0.7.1)
Befunde aus dem ersten echten Erstinstallations-Test auf frischen Debian-12-
und Debian-13-Containern. Der Weg selbst hat getragen (Basis-Install,
Instanz-Anlage, zweite Instanz, Update mit Rauchtest, Rollback) — diese
Stellen haben gelogen oder gefehlt:

- Das Ghostscript-Backports-Angebot auf Debian 12 war ein garantierter
  Leerlauf, der "aktualisiert ✓" meldete: bookworm-backports enthaelt gar
  kein ghostscript (am Paketindex verifiziert). Die Routine sucht jetzt den
  echten Kandidaten, vergleicht vorher/nachher und raeumt eine nur zur Probe
  angelegte Quelle wieder weg.
- Derselbe untaugliche Rat stand in der Preflight-Meldung, der
  pdfa_level-Warnung, config.example.toml und vier Doku-Dateien — ueberall
  ersetzt durch die echten Optionen.
- Mehrzeilige Log-Hinweise waren durch "echo -e" zerrissen und nicht
  kopierbar; log_* nutzt jetzt printf mit %s.
- pip-freeze.txt landete beim Rollback als /pip-freeze.txt im
  Wurzelverzeichnis, liegt jetzt unter opt/pdf-ocr-hotfolder/.
- git und sudo fehlen auf dem Proxmox-Debian-Template; "sudo ./install.sh"
  scheitert dort. Beide Wege dokumentiert, git als Voraussetzung ergaenzt,
  HTTPS-Clone als Normalfall.
- Rollback: systemctl start kann kein Glob. journald-Reparatur: Instanzen
  danach neu starten, sonst bleibt das Journal leer.

254 Tests gruen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 01:36:42 +02:00

8.7 KiB

Debian-Major-Upgrade

Wie der Hotfolder ein Distributions-Upgrade übersteht (12 → 13, später 13 → 14).

Verwandte Dokumente: README · Installation · Update


sudo oder direkt als root. Alle Befehle dieser Seite brauchen root-Rechte, nicht sudo. Wer als root arbeitet — im Proxmox-Debian-Standard-Template der Normalfall, dort ist sudo gar nicht installiert —, lässt das sudo einfach weg. Siehe INSTALLATION.md.


Warum das ein eigener Ablauf ist

Die venv unter /opt/pdf-ocr-hotfolder/venv/ hängt an der Python-Version der Distribution. Ein apt full-upgrade von Debian 12 auf 13 tauscht Python 3.11 gegen 3.13 aus. Danach zeigt venv/bin/python auf einen Interpreter, den es so nicht mehr gibt — systemd quittiert den Start jeder Instanz mit 203/EXEC, und auch wenn der Interpreter noch existiert, passen die installierten Pakete nicht mehr zum System-Python.

Die venv muss nach dem Sprung neu gebaut werden. Ein normales sudo ./update.sh genügt dafür nicht sicher genug — es gibt den ausdrücklichen Schalter --rebuild-venv.


Der Ablauf

1. Vorher updaten

cd /pfad/zum/repo
git pull
sudo ./update.sh

Das bringt die Installation auf den aktuellen Stand und erzeugt vor allem ein frisches Backup inklusive pip-freeze.txt — die Liste der Paketversionen, die auf dem alten System liefen. Inhalt und Ort des Backups: UPDATE.md.

2. Instanzen stoppen

sudo systemctl stop 'pdf-ocr-hotfolder@*'

Während des Upgrades darf kein OCR laufen: Ghostscript, Tesseract und die Python-Pakete werden mitten im Betrieb ausgetauscht.

Geduld. Die Unit hat TimeoutStopSec=300, damit ein laufendes OCR sauber zu Ende kommt. Ein Stop kann pro Instanz bis zu 5 Minuten dauern — bei mehreren Instanzen entsprechend länger. Nicht mit kill -9 nachhelfen; ein harter Stopp lässt das Original in working/ liegen (der Dienst nimmt es beim nächsten Start zwar wieder auf, aber der Durchlauf ist verloren).

Prüfen, dass wirklich alles steht:

systemctl status 'pdf-ocr-hotfolder@*'

3. Distribution upgraden

Der übliche Debian-Weg — Sources auf das neue Release umstellen, dann:

sudo apt update
sudo apt full-upgrade
sudo reboot

Die Instanzen sind enabled und starten nach dem Reboot mit; mit der alten venv scheitern sie (203/EXEC). Das ist erwartet und wird im nächsten Schritt behoben.

4. venv neu bauen

cd /pfad/zum/repo
git pull
sudo ./update.sh --rebuild-venv

--rebuild-venv erzwingt den Neubau. Der Rest des Updates läuft wie gewohnt (siehe UPDATE.md) — die System-Pakete werden dabei ebenfalls abgeglichen, auch python3-venv für das neue Python.

Der Neubau ist ganz oder gar nicht:

  1. Die alte venv wird nach venv.old-<timestamp> verschoben.
  2. python3 -m venv baut neu.
  3. Die Requirements werden installiert.
  4. Erst bei Erfolg wird die alte venv gelöscht.

5. Wenn ein Pin nicht mehr passt

Scheitert pip install an einer gepinnten Version, bricht das Skript ab und sagt genau, was los ist:

  • die letzten 25 Zeilen der pip-Ausgabe,
  • das gescheiterte Paket ("Gescheitertes Paket: …"),
  • die Diagnose: "eine in requirements.txt fest gepinnte Version gibt es für Python <version> nicht (mehr)",
  • den nächsten Schritt: requirements.txt anheben und update.sh --rebuild-venv erneut laufen lassen.

Die alte venv ist dann zurückgerollt — es liegt keine halb gefüllte venv herum. Sie hängt zwar weiterhin am alten Interpreter und die Instanzen laufen damit nicht (das sagt das Skript auch), aber der Zustand ist eindeutig.

Also:

# im Repo, auf einer Testmaschine
vim requirements.txt          # Version des genannten Pakets anheben
pytest                        # Suite muss grün bleiben
git commit -am 'requirements: <paket> auf <version> anheben'
git push

# auf dem Zielsystem
git pull
sudo ./update.sh --rebuild-venv

6. --rebuild-venv vergessen?

Halb so wild: update.sh prüft die venv auch ohne den Schalter und baut sie bei Versions-Drift von selbst neu. Geprüft wird

  • ob das Verzeichnis und venv/bin/python überhaupt existieren,
  • ob der Interpreter der venv noch läuft (toter Symlink nach dem Upgrade),
  • ob seine major.minor zum System-python3 passt,
  • ob pyvenv.cfg dieselbe Version nennt wie der Interpreter.

Stimmt eines davon nicht, nennt das Skript den Grund und baut neu. --rebuild-venv ist also nicht der einzige, aber der ausdrückliche Weg — und der, den man nach einem Distributions-Upgrade nimmt, statt sich auf die Erkennung zu verlassen.


Danach prüfen

Laufen alle Instanzen?

systemctl status 'pdf-ocr-hotfolder@*'

update.sh hat das schon verifiziert (Wartezeit, is-failed, Crash-Loop) und in der Zusammenfassung Soll gegen Ist gestellt — ein Exit 0 heißt, dass jede Instanz, die vorher lief, auch wieder läuft.

Sind die Configs sauber?

cd /opt/pdf-ocr-hotfolder
for f in /etc/pdf-ocr-hotfolder/*.toml; do
    sudo ./venv/bin/python -m pdf_ocr_hotfolder --check-config --config "$f"
done

Exit 0/1/2 und was bei Warnungen zu tun ist: UPDATE.md.

Läuft eine echte PDF durch?

cp test.pdf /var/lib/pdf-ocr-hotfolder/<instanz>/incoming/
journalctl -u pdf-ocr-hotfolder@<instanz> -f

Im outgoing/ muss das OCR-PDF liegen, im Journal steht OCR done. Das ist der einzige Test, der die neue venv und die neuen System-Binaries (Tesseract, Ghostscript) wirklich anfasst — systemctl status sagt darüber nichts.

Ghostscript-Version auf dem neuen Release ansehen:

gs --version

Auf Debian 13 ist der Ghostscript-Bug aus Debian 12 kein Thema mehr — Debian 13 liefert Ghostscript 10.05.1. Damit ist PDF/A ([ocr].pdfa_level = "1", "2" oder "3") zusammen mit skip_text = true erstmals ohne Einschränkung nutzbar; auf Debian 12 gab es dafür keinen Weg (ein Upgrade aus bookworm-backports existiert nicht, dort liegt kein Ghostscript-Paket). Genau das ist oft der Grund für das Upgrade. Hintergrund: INSTALLATION.md.

Hat jemand auf dem alten System nach der früheren, falschen Empfehlung einen bookworm-backports-Eintrag unter /etc/apt/sources.list.d/ angelegt, gehört er nach dem Upgrade entfernt — Ghostscript kam ohnehin nie daher:

sudo rm -f /etc/apt/sources.list.d/bookworm-backports.list
sudo apt update

Pins in requirements.txt

Die Python-Abhängigkeiten sind bewusst fest gepinnt:

ocrmypdf==17.4.1
watchdog==6.0.0
requests==2.33.1
paramiko==4.0.0

Ohne Pins würde ein pip install --upgrade bei jedem Update ungefragt eine neue Major-Version ziehen — der nächste Sprung wäre ocrmypdf 17 auf 18, und der reißt sonst alle Instanzen auf einmal, und zwar im Moment des Updates, nicht zu einem Zeitpunkt, den man sich ausgesucht hat.

⚠️ ocrmypdf darf nicht unter 17 fallen. Bis einschließlich 16.x prüft ocrmypdf die Ghostscript-Version auch dann, wenn gar kein PDF/A erzeugt wird — auf Debian 12 (Ghostscript 10.0.0) scheitert damit jede PDF, weil skip_text = true unser Default ist. Genau das war der Ausfall in 0.6.0. Hintergrund: INSTALLATION.md.

Die aktuellen Pins sind gegen Python 3.11 (Debian 12) und 3.13 (Debian 13) geprüft; für beide gibt es fertige Wheels, es wird nichts kompiliert. Das gilt auch für die Abhängigkeiten, die ocrmypdf 17 zusätzlich mitbringt (pydantic, pypdfium2, fpdf2, uharfbuzz).

Beim Anheben:

  1. Testmaschine benutzen — nie direkt auf dem produktiven Hotfolder.
  2. Prüfen, dass es für die neue Version auf beiden Python-Versionen fertige Wheels gibt, sonst wird auf dem Zielsystem kompiliert:
    pip install --dry-run --only-binary=:all: --python-version 3.11 \
        --target /tmp/wheelcheck ocrmypdf==<version>
    pip install --dry-run --only-binary=:all: --python-version 3.13 \
        --target /tmp/wheelcheck ocrmypdf==<version>
    
  3. Dort update.sh --rebuild-venv fahren, damit die Pakete wirklich frisch aufgelöst werden.
  4. pytest muss grün bleiben (254 Tests).
  5. Eine echte PDF durchschieben — die Test-Suite mockt ocrmypdf, ein Major-Sprung fällt dort also nicht auf. Der Rauchtest in update.sh macht genau das automatisch.
  6. Erst dann committen und auf die produktiven Systeme geben.

Der ocrmypdf-Sprung 16 → 17 ist ein Major-Sprung und gehört in einen eigenen Vorgang mit eigenem Test, nicht in ein OS-Upgrade.