# Das Übergabeprotokoll als PDF Jeder Lauf lässt sich als PDF herunterladen — ein Blatt mit Kopfdaten, allen Schritten samt Zuständen und Notizen, den Kommentaren und zwei Unterschriftsfeldern. Gedacht ist es für die Übergabe: ein Gerät wechselt den Besitzer, beide unterschreiben, das Blatt geht in die Ablage oder zum Kunden. **Das ist eine Zusatzausstattung, und sie kostet.** Ohne sie fehlt nichts Wesentliches: die Druckansicht zeigt dasselbe Blatt, und jeder Browser kann daraus „in eine Datei drucken“. Das PDF vom Server nimmt diesen Schritt nur ab — für den, der zwanzig Protokolle am Stück braucht, oder für den, der sie aus dem Archiv heraus einzeln verschickt. ## Was es braucht **Chromium auf dem Server.** Nichts weiter — keine Schriften aus dem Netz, kein Dienst, kein zusätzlicher Port. Der Grund: das PDF entsteht, indem der Server seine **eigene** Druckansicht lädt, so wie ein Browser es täte, und sie druckt. Dadurch gibt es genau eine Vorlage für Papier und PDF. Ein zweites Layout im Code wäre billiger zu betreiben und würde früher oder später von der Druckansicht abweichen, ohne dass es jemandem auffällt — bis ein Kunde zwei verschiedene Blätter nebeneinander legt. Chromium ist der Preis dafür, und er ist hoch: | Laufzeitbild | Größe | |---|---| | ohne Chromium | **370 MB** | | mit Chromium | **1,52 GB** | Gemessen am 2026-10-25. Alpines `chromium` kostet allein 1,13 GB — mehr als das Dreifache der gesamten Anwendung. Deshalb ist er nicht im Standardbild, und deshalb ist diese Seite kein Anhang, sondern eine Entscheidung. ## Mit Docker Compose Chromium kommt über ein Bau-Argument ins Bild: ```bash docker compose build --build-arg MIT_PDF=1 web docker compose up -d ``` Damit das beim nächsten `docker compose build` nicht verlorengeht, gehört es in die `docker-compose.yml` beziehungsweise in eine Override-Datei: ```yaml services: web: build: args: MIT_PDF: "1" ``` Mehr ist nicht zu tun: das Paket liegt dann unter `/usr/bin/chromium-browser`, und dort sucht die Anwendung von sich aus. ## Nativ, im LXC oder auf einem Debian `deploy/install.sh` fragt beim ersten Lauf, ob Chromium mit soll, und merkt die Antwort in `/etc/laufzettel/laufzettel.env`. Nachträglich: ```bash apt-get install -y chromium systemctl restart laufzettel ``` Auf Debian 13 liegt es danach unter `/usr/bin/chromium`. Der Neustart ist nötig, weil die Anwendung den Pfad beim ersten PDF sucht und sich das Ergebnis für die Laufzeit des Prozesses merkt. ## Wenn Chromium fehlt **Dann passiert nichts Stilles.** Der Knopf „Als PDF“ erscheint am Lauf und im Archiv gar nicht erst — die Anwendung sieht bei jedem Seitenaufbau nach, ob ein Chromium da ist. Wer den Endpunkt trotzdem aufruft, etwa aus einem alten Tab oder mit einem Lesezeichen, bekommt `503` und diesen Text: > Auf diesem Server ist kein Chromium installiert, und ohne ihn entsteht kein PDF. Die Druckansicht > funktioniert weiterhin: dort im Browser „in eine Datei drucken“. Wie Chromium dazukommt, steht in > docs/betrieb/pdf.md. Ein leeres oder halbes PDF gibt es in keinem Fall. Das war die Vorgabe für dieses Verhalten: ein Blatt, das aussieht wie ein Protokoll und keines ist, wäre schlimmer als eine klare Absage. ## Die drei Einstellungen Alle drei sind Vorgaben, die man kennen sollte und selten braucht. | Variable | Vorgabe | Wofür | |---|---|---| | `CHROMIUM_PFAD` | leer | Wo Chromium liegt, wenn er woanders liegt | | `PDF_MAX_GLEICHZEITIG` | `2` | Wie viele PDFs gleichzeitig entstehen dürfen | | `PDF_LEERLAUF_MS` | `60000` | Nach wie viel Leerlauf der Browser wieder schließt | **`CHROMIUM_PFAD` leer lassen heißt „suche selbst“** — der Reihe nach `/usr/bin/chromium-browser`, `/usr/bin/chromium`, `/usr/bin/google-chrome`. Ein gesetzter Pfad, der ins Leere zeigt, ist dagegen ein **Fehler** und kein Rückfall auf die Suche: wer hier etwas einträgt, hat eine Absicht, und ein Tippfehler soll auffallen und nicht dazu führen, dass stillschweigend ein anderer Browser läuft als der gemeinte. **`PDF_MAX_GLEICHZEITIG` schützt den Arbeitsspeicher.** Jedes PDF hält kurz einen eigenen Browser-Kontext offen — eigen, und nicht geteilt, weil er das Sitzungscookie des Anfragenden trägt. Ist die Grenze erreicht, bekommt der nächste die Bitte, es gleich noch einmal zu versuchen, statt einer Maschine, die unter Last steht. **`PDF_LEERLAUF_MS`** hält den Chromium nach dem letzten PDF noch eine Minute offen, weil ihn zu starten rund zwei Sekunden kostet und Protokolle meist in Serie entstehen. Danach schließt er: offen belegt er rund 100 MB, und auf einem 2-GB-LXC ist das ein Achtel des Arbeitsspeichers für etwas, das niemand gerade braucht. ## Was auf dem Blatt steht — und was nicht Es steht dasselbe darauf wie in der Druckansicht, denn es **ist** die Druckansicht. Insbesondere: - **Kein generiertes Passwort.** Wo ein Befehl `{{generiertes_passwort}}` enthält, steht auf dem Blatt `[Passwort – nicht gespeichert]`. Das Passwort entsteht bei jeder Anzeige neu und wird nirgends gespeichert; ein Ausdruck dagegen liegt herum. Geprüft wird das am extrahierten Text des fertigen PDF, nicht an der Vorlage. - **Keine Anhänge**, nur ihre Anzahl. Ein Foto gehört nicht auf ein Übergabeprotokoll, und ein PDF mit zwanzig Bildern wäre nicht mehr zu verschicken. - **Befehle unmaskiert.** Das Blatt hält fest, was zu tun war; Maskierzeichen wären auf Papier Rauschen. Wer einen Befehl ausführen will, nimmt die Laufseite — dort ist er für die gewählte Shell maskiert. ## Fehlersuche **Der Knopf fehlt, obwohl Chromium installiert ist.** Die Anwendung hat ihn nicht gefunden. Nachsehen, wo er liegt, und `CHROMIUM_PFAD` setzen: ```bash command -v chromium chromium-browser google-chrome ``` Danach den Dienst neu starten. Im Compose-Stand prüft man im Container selbst, nicht auf dem Host: ```bash docker compose exec web command -v chromium-browser ``` **`503` mit „Die Druckansicht antwortete mit 307“** (oder 302, oder 401). Das Sitzungscookie kam nicht mit oder gilt nicht mehr — Chromium wurde auf die Anmeldeseite geschickt. Neu anmelden und es noch einmal versuchen. Bleibt es dabei, steht vermutlich ein Reverse Proxy im Weg, der `Cookie` nicht durchlässt. **`503` mit „Es entstehen gerade … PDFs gleichzeitig“.** Die Grenze greift. Entweder warten oder `PDF_MAX_GLEICHZEITIG` anheben — und dabei im Blick behalten, dass jeder gleichzeitige Vorgang Arbeitsspeicher kostet. **`500` mit „Einzelheiten stehen im Protokoll des Dienstes“.** Hier geht die Meldung absichtlich nicht hinaus, weil eine rohe Ausnahme Pfade und Zugangsdaten nennen kann. Nachsehen: ```bash docker compose logs -f web | grep PDF # Compose journalctl -u laufzettel -f | grep PDF # nativ ``` **Leere Kästen statt Buchstaben im PDF.** Dem Chromium fehlen Systemschriften. Im Bild ist `font-noto` deshalb mit dabei; nativ gehört es nachinstalliert (`apt-get install -y fonts-noto-core`). Die Schriften der Oberfläche selbst liefert die Anwendung mit, aber für Zeichen, die darin nicht vorkommen, braucht Chromium einen Rückfall. ## Wo sonst noch etwas steht - [`../../.env.example`](../../.env.example) — die drei Variablen mit Kommentar - [`inbetriebnahme.md`](inbetriebnahme.md) — der Compose-Weg im Ganzen - [`proxmox.md`](proxmox.md) — der native Weg und `deploy/install.sh` - [`../anleitung/02-laeufe-abarbeiten.md`](../anleitung/02-laeufe-abarbeiten.md) — wie man das Protokoll als Anwender holt