Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Cloud / Hosting 05.07.2026 · 9 min Lesezeit

Gotenberg mit Docker installieren: Entwicklerfreundliche Docker-API für PDF-Konvertierung

Gotenberg ist eine zustandslose REST-API im Docker-Container, die HTML, DOCX, XLSX, PPTX und 100+ weitere Formate per einfachem multipart-POST in PDFs verwandelt – ohne LibreOffice oder Chromium auf dem Host. Diese Anleitung zeigt den kompletten Weg von compose.yaml bis zum ersten curl-Test.

Illustration zur Installation von Gotenberg mit Docker. Die Grafik zeigt einen Docker-Container, einen Linux-Server, ein Terminal sowie die Konvertierung von HTML-, Markdown- und Office-Dokumenten in PDF über eine Docker-API. Titelbild für eine technische

Wer in einer Anwendung PDF-Dateien erzeugen muss, kennt das Problem: LibreOffice auf dem Server installieren, Headless-Chromium konfigurieren, Abhängigkeiten verwalten – und das auf jedem Deployment-Ziel erneut. Gotenberg löst genau dieses Problem. Die in Go geschriebene REST-API verpackt Headless Chromium und LibreOffice in einen einzigen Docker-Container. Entwickler schicken Dateien per multipart/form-data an den Container und erhalten ein fertiges PDF zurück. Mit über 12.000 GitHub-Stars und 1,4 Millionen Docker-Pulls pro Woche ist Gotenberg das meistgenutzte Werkzeug dieser Art – und der Betrieb erfordert weder Datenbank noch zusätzliche Services.

Voraussetzungen

  1. Docker Engine 24.x oder neuer mit Docker Compose Plugin v2 (docker compose ohne Bindestrich) – wie du das auf einem Linux-Host einrichtest, erklärt die Anleitung Docker und Docker Compose auf Linux installieren.
  2. Beliebiger Linux-Host, VM oder NAS mit Docker-Unterstützung (amd64, arm64, armv7, Raspberry Pi, Apple Silicon werden alle unterstützt).
  3. Mindestens 512 MB freier RAM; bei regelmäßigen LibreOffice-Konvertierungen unter Last lieber 1–2 GB reservieren.
  4. curl zum Testen der API – auf praktisch allen Linux-Systemen vorinstalliert.
  5. Optional: Reverse Proxy (Caddy, Nginx, Traefik) für TLS-Terminierung, wenn der Dienst von außen erreichbar sein soll. Die Anleitung Caddy als Reverse Proxy einrichten zeigt den Weg.
  6. Eine Testdatei (.docx oder .html) für die Verifikation nach dem Start.

Schritt 1: Projektordner anlegen

Lege einen dedizierten Ordner für den Gotenberg-Stack an. Alle Konfigurationsdateien liegen hier – mehr braucht Gotenberg nicht, da es vollständig zustandslos ist und keine Datenbank oder persistenten Volumes benötigt.

mkdir -p /opt/gotenberg
cd /opt/gotenberg

Wer den Stack im Home-Verzeichnis bevorzugt, nimmt entsprechend ~/gotenberg.

Verifizieren: ls /opt/gotenberg – der Ordner ist vorhanden. Der Befehl gibt keine Fehlermeldung zurück.

Schritt 2: .env-Datei anlegen

Die .env-Datei enthält alle anpassbaren Werte. Sensible Zugangsdaten für Basic Auth gehören hier rein – sie werden nie direkt in die compose.yaml geschrieben.

# /opt/gotenberg/.env

# API-Konfiguration
API_TIMEOUT=60s
API_BODY_LIMIT=20MB

# Basic Auth (in Produktion auf true setzen)
API_ENABLE_BASIC_AUTH=false
GOTENBERG_API_BASIC_AUTH_USERNAME=admin
GOTENBERG_API_BASIC_AUTH_PASSWORD=sicheres_passwort_hier_aendern

# Logging
LOG_LEVEL=info
LOG_STD_FORMAT=auto

# Chromium-Modul
CHROMIUM_MAX_CONCURRENCY=6
CHROMIUM_RESTART_AFTER=100
CHROMIUM_AUTO_START=false

# LibreOffice-Modul
LIBREOFFICE_RESTART_AFTER=10
LIBREOFFICE_AUTO_START=false

Für Produktionsumgebungen: API_ENABLE_BASIC_AUTH=false auf true ändern und ein starkes Passwort setzen, dann docker compose up -d erneut ausführen. Wer sehr große DOCX- oder XLSX-Dateien konvertiert, sollte API_TIMEOUT auf 120s oder mehr erhöhen – der Standard von 30 Sekunden (hier bereits auf 60s erweitert) reicht für große Dokumente manchmal nicht aus.

Verifizieren: cat /opt/gotenberg/.env – alle Variablen sind korrekt eingetragen, keine Syntaxfehler.

Schritt 3: compose.yaml erstellen

Die folgende compose.yaml startet Gotenberg mit Ressourcenbegrenzung, Health-Check und sicherem Port-Binding. Das Image-Tag :8 folgt immer dem neuesten stabilen 8.x-Patch (aktuell v8.34.0) – wer exakt pinnen will, verwendet gotenberg/gotenberg:8.34.0.

# /opt/gotenberg/compose.yaml
services:
  gotenberg:
    image: gotenberg/gotenberg:8
    container_name: gotenberg
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      API_TIMEOUT: ${API_TIMEOUT:-60s}
      API_BODY_LIMIT: ${API_BODY_LIMIT:-20MB}
      API_ENABLE_BASIC_AUTH: ${API_ENABLE_BASIC_AUTH:-false}
      GOTENBERG_API_BASIC_AUTH_USERNAME: ${GOTENBERG_API_BASIC_AUTH_USERNAME:-}
      GOTENBERG_API_BASIC_AUTH_PASSWORD: ${GOTENBERG_API_BASIC_AUTH_PASSWORD:-}
      LOG_LEVEL: ${LOG_LEVEL:-info}
      LOG_STD_FORMAT: ${LOG_STD_FORMAT:-auto}
      CHROMIUM_MAX_CONCURRENCY: ${CHROMIUM_MAX_CONCURRENCY:-6}
      CHROMIUM_RESTART_AFTER: ${CHROMIUM_RESTART_AFTER:-100}
      CHROMIUM_AUTO_START: ${CHROMIUM_AUTO_START:-false}
      LIBREOFFICE_RESTART_AFTER: ${LIBREOFFICE_RESTART_AFTER:-10}
      LIBREOFFICE_AUTO_START: ${LIBREOFFICE_AUTO_START:-false}
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: "1.0"
        reservations:
          memory: 512M
          cpus: "0.2"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

Wichtige Designentscheidung: Der Port ist bewusst auf 127.0.0.1:3000:3000 beschränkt. Gotenberg hat im Standardmodus keine Authentifizierung – ein versehentlich freigegebener Port würde jedem erlauben, beliebige Konvertierungen anzustoßen. Sollen andere Docker-Services im gleichen Stack auf Gotenberg zugreifen, brauchen sie gar keinen exportierten Port: Sie erreichen den Container intern über http://gotenberg:3000.

Eine ausführliche Erklärung zu Docker-Netzwerken und der internen Service-Kommunikation findest du in der Anleitung Docker-Netzwerke und Volumes richtig nutzen.

Verifizieren: docker compose config im Projektordner zeigt die aufgelöste Konfiguration inklusive der .env-Werte – keine Fehlermeldung, alle Variablen befüllt.

Schritt 4: Container starten

Vom Projektordner aus den Stack starten:

cd /opt/gotenberg
docker compose up -d

Docker lädt das Image herunter (ca. 1,5 GB für das Vollimage mit Chromium und LibreOffice) und startet den Container im Hintergrund. Der erste Start kann je nach Internetverbindung einige Minuten dauern.

Status und Logs prüfen:

# Container-Status anzeigen
docker compose ps

# Logs verfolgen (Ctrl+C zum Beenden)
docker compose logs -f gotenberg

Erwartete Ausgabe von docker compose ps:

NAME        IMAGE                    STATUS
gotenberg   gotenberg/gotenberg:8    Up 30 seconds (healthy)

In den Logs sollte nach wenigen Sekunden eine Zeile ähnlich dieser erscheinen:

{"level":"INFO","msg":"server listening","port":3000}

Verifizieren: docker compose ps zeigt STATUS = Up ... (healthy). Sollte der Status (health: starting) zeigen, kurz warten und erneut prüfen – der Health-Check hat 10 Sekunden Anlaufzeit.

Schritt 5: API testen – Health-Check und erste Konvertierung

Zuerst den Health-Endpunkt aufrufen:

curl -I http://localhost:3000/health

Erwartete Antwort:

HTTP/1.1 200 OK
Content-Type: application/json

Alternativ im Browser: http://localhost:3000/health – dort erscheint ein JSON-Objekt mit dem Status beider Module (Chromium, LibreOffice).

Eine Webseite per URL in PDF konvertieren

curl --request POST \
  http://localhost:3000/forms/chromium/convert/url \
  --form url=https://example.com \
  -o /tmp/example-com.pdf

Eine DOCX-Datei in PDF konvertieren

curl --request POST \
  http://localhost:3000/forms/libreoffice/convert \
  --form files=@/pfad/zu/dokument.docx \
  -o /tmp/ausgabe.pdf

Eine lokale HTML-Datei in PDF konvertieren

curl --request POST \
  http://localhost:3000/forms/chromium/convert/html \
  --form files=@/pfad/zu/seite.html \
  -o /tmp/seite.pdf

Wenn Basic Auth aktiv ist (API_ENABLE_BASIC_AUTH=true), muss bei jedem curl-Aufruf --user admin:passwort ergänzt werden.

Verifizieren: Die erzeugten PDF-Dateien unter /tmp/ sind vorhanden (ls -lh /tmp/*.pdf) und lassen sich öffnen. Eine leere oder 0-Byte-Datei deutet auf einen Konvertierungsfehler hin – dann docker compose logs gotenberg auf Fehlermeldungen prüfen.

Schritt 6: Eckdaten im Überblick

ParameterWertHinweis
Imagegotenberg/gotenberg:8Vollimage (Chromium + LibreOffice); aktuell v8.34.0
Image-Varianten:8-chromium, :8-libreoffice~30–40 % kleiner, jeweils nur ein Modul
Port3000/tcpNur auf localhost binden: 127.0.0.1:3000:3000
VolumeskeineZustandslos, keine persistenten Daten nötig
DatenbankkeineEinziger Container genügt
Architekturamd64, arm64, armv7, i386, ppc64leRaspberry Pi und Apple Silicon unterstützt
RAM (min.)512 MBUnter LibreOffice-Last eher 1–2 GB
Chromium-Parallelitätbis 6 (einstellbar)CHROMIUM_MAX_CONCURRENCY
LibreOffice-Parallelität1 (sequenziell)Bei hoher Last: mehrere Container-Instanzen
API-EndpunktModulUnterstützte Formate (Auswahl)
POST /forms/chromium/convert/urlChromiumBeliebige URL → PDF
POST /forms/chromium/convert/htmlChromiumHTML-Datei(en) → PDF
POST /forms/chromium/convert/markdownChromiumMarkdown + HTML-Template → PDF
POST /forms/libreoffice/convertLibreOfficeDOCX, XLSX, PPTX, ODT, ODS, RTF, CSV und 100+ weitere
GET /healthStatus beider Module, 200 OK wenn bereit

Schritt 7: Updates einspielen

Da Gotenberg zustandslos ist und keine Volumes hat, ist ein Update denkbar einfach:

cd /opt/gotenberg
docker compose pull
docker compose up -d

Docker zieht das neue Image, stoppt den laufenden Container und startet ihn mit dem aktualisierten Image neu. Downtime: wenige Sekunden. Wer Container-Updates automatisieren möchte, findet in der Anleitung Watchtower mit Docker: Container automatisch aktualisieren eine praktische Lösung.

Verifizieren: Nach dem Update docker compose ps und curl -I http://localhost:3000/health – Status muss wieder 200 OK und healthy sein.

Schritt 8: Optional – Basic Auth und Reverse Proxy aktivieren

Soll Gotenberg über das Internet erreichbar sein, sind zwei Schritte Pflicht:

Basic Auth aktivieren: In der .env die Zeile API_ENABLE_BASIC_AUTH=false auf true ändern und ein starkes Passwort setzen, dann docker compose up -d erneut ausführen. Alle curl-Anfragen müssen dann --user admin:passwort enthalten.

Reverse Proxy mit TLS vorschalten: Gotenberg selbst terminiert kein TLS. Für HTTPS einen Reverse Proxy (Caddy, Nginx Proxy Manager oder Traefik) vorschalten. Der Proxy leitet Anfragen an http://gotenberg:3000 weiter – bei gemeinsamen Docker-Netzwerken ohne exportierten Port.

Für Docker Compose Stacks, die Secrets aus einer .env sicher handhaben und Healthchecks korrekt einsetzen, gibt es weitere Best Practices in der Anleitung Docker Compose absichern: Secrets, Healthchecks, Non-Root und Read-Only.

Verifizieren: Mit aktivierter Basic Auth einen curl-Test ohne Credentials durchführen – der Server antwortet mit 401 Unauthorized. Mit --user admin:passwort erneut testen – jetzt kommt 200 OK zurück.

Troubleshooting / Typische Fehler

  1. 503 Supervisor run task: context deadline exceeded: Das Timeout wurde überschritten. Ursache: großes DOCX/XLSX oder langsamer Host. Lösung: API_TIMEOUT=120s in der .env setzen und docker compose up -d neu starten.
  2. Layout-Verschiebungen bei DOCX-zu-PDF: Microsoft Core Fonts (Arial, Calibri, Times New Roman) fehlen aus Lizenzgründen. Lösung: eigenes Dockerfile auf Basis FROM gotenberg/gotenberg:8 erstellen und die gewünschten Fonts per apt-get oder COPY hinzufügen. Ein Volume-Mount funktioniert hier nicht.
  3. Port versehentlich öffentlich freigegeben: Wenn 0.0.0.0:3000:3000 statt 127.0.0.1:3000:3000 gesetzt ist, ist Gotenberg ohne Auth von außen erreichbar. Sofort korrigieren und docker compose up -d neu starten.
  4. HTML referenziert http://localhost:8080 – kein Zugriff möglich: Im Container zeigt localhost auf Gotenberg selbst, nicht auf den Docker-Host. Lösung: Docker-interne DNS-Namen verwenden (http://mein-app-service:8080) oder unter Docker Desktop host.docker.internal nutzen.
  5. Bilder und CSS fehlen im generierten PDF: Externe Ressourcen müssen per erreichbarer URL eingebunden oder als zusätzliche Dateien im multipart-Request mitgeschickt werden (--form files=@style.css --form files=@logo.png).
  6. Chromium Print Error -32000 / Speichermangel: Sehr große HTML-Seiten überfordern Chromium. Lösung: deploy.resources.limits.memory auf 2G erhöhen.
  7. LibreOffice-Timeouts unter Last: LibreOffice läuft als einzelne Instanz mit Lock – Anfragen stellen sich sequenziell in die Warteschlange. Lösung: mehrere Gotenberg-Container hinter einem Load Balancer betreiben.
  8. Fehler nicht lesbar: LOG_LEVEL=debug setzen. Bei HTTP-400-Fehlern immer den Response-Body lesen – Gotenberg gibt dort das fehlerhafte Formularfeld im Klartext an.

Häufige Fragen

Braucht Gotenberg eine Datenbank oder Redis?

Nein. Gotenberg ist vollständig zustandslos – kein Redis, keine SQL-Datenbank, keine externe Abhängigkeit. Temporäre Dateien werden intern verwaltet und beim Container-Neustart gelöscht. Ein einzelner Container genügt.

Welches Image-Tag sollte ich verwenden?

Für Reproduzierbarkeit empfiehlt sich gotenberg/gotenberg:8 – dieses Tag folgt automatisch dem neuesten stabilen 8.x-Patch (aktuell v8.34.0). Das :latest-Tag kann auf eine neue Hauptversion springen und ist für den Produktionsbetrieb nicht empfohlen. Wer exakt pinnen will, nimmt gotenberg/gotenberg:8.34.0.

Kann ich nur HTML oder nur Office-Dokumente konvertieren – ohne das große Vollimage?

Ja. Gotenberg bietet zwei schlankere Varianten: gotenberg/gotenberg:8-chromium (nur Chromium, ca. 30 % kleiner – für HTML, URL, Markdown) und gotenberg/gotenberg:8-libreoffice (nur LibreOffice, ca. 40 % kleiner – für Office-Formate). Für den Einstieg und gemischte Workloads ist das Vollimage :8 die sichere Wahl.

Kann Gotenberg mehrere Dokumente gleichzeitig konvertieren?

Das hängt vom Modul ab. Chromium unterstützt bis zu 6 parallele Konvertierungen (einstellbar über CHROMIUM_MAX_CONCURRENCY). LibreOffice läuft als einzelne Instanz mit Lock – Anfragen werden sequenziell verarbeitet. Bei hoher LibreOffice-Last hilft nur horizontales Skalieren: mehrere Gotenberg-Container hinter einem Load Balancer.

Wie aktiviere ich Authentifizierung?

In der .env API_ENABLE_BASIC_AUTH=true setzen sowie GOTENBERG_API_BASIC_AUTH_USERNAME und GOTENBERG_API_BASIC_AUTH_PASSWORD mit echten Werten belegen, dann docker compose up -d. Bei curl-Anfragen --user benutzername:passwort ergänzen. Für den Produktionseinsatz ist zusätzlich ein Reverse Proxy mit TLS empfohlen.

Läuft Gotenberg auf ARM-Hardware (Raspberry Pi, Apple Silicon)?

Ja. Das offizielle Image unterstützt amd64, arm64v8, arm32v7, i386 und ppc64le. Docker wählt beim Pull automatisch das passende Manifest aus – kein manueller Eingriff nötig.

Kann ich Webhooks nutzen, um auf Konvertierungsergebnisse zu warten?

Ja. Gotenberg unterstützt asynchrone Verarbeitung via Webhook: Den HTTP-Header Gotenberg-Webhook-Url mit der Rückruf-URL mitgeben. Gotenberg sendet das fertige PDF per POST an diese URL, sobald die Konvertierung abgeschlossen ist – nützlich bei großen Dokumenten, die länger brauchen als ein synchrones Request-Timeout erlaubt.

Fazit

Gotenberg ist der schnellste Weg, eine skalierbare PDF-Konvertierung in einen bestehenden Workflow zu integrieren. Ein einzelner Container, keine Datenbank, keine Host-Abhängigkeiten – das macht Deployments auf unterschiedlichen Plattformen unkompliziert. Die REST-API ist bewusst einfach gehalten: multipart-POST rein, PDF raus. Wer HTML-zu-PDF und Office-Konvertierung in einer Anwendung braucht, findet in Gotenberg eine deutlich wartungsärmere Lösung als selbst installierte LibreOffice- oder wkhtmltopdf-Setups. Einen Punkt sollte man im Blick behalten: die sequenzielle Natur von LibreOffice unter Last – bei skalierenden Workloads früh über mehrere Container-Instanzen nachdenken.

Weiterführende Anleitungen und Quellen

  1. Paperless-ngx mit Docker einrichten: papierlose Dokumentenverwaltung mit OCR – ergänzt Gotenberg ideal für automatisierte Dokumenten-Workflows.
  2. Stirling-PDF mit Docker: der lokale PDF-Werkzeugkasten – für manuelle PDF-Operationen im Browser statt per API.
  3. Docker und Docker Compose auf Linux installieren: die Self-Hosting-Grundlage
  4. Caddy als Reverse Proxy einrichten: Anfänger-Anleitung mit automatischem HTTPS

Offizielle Quellen: Gotenberg Dokumentation und Gotenberg auf GitHub.

Passende Anleitungen auf S-EDV

  1. netcup Local Block Storage bestellen, einrichten und unter Linux einbinden
  2. Linux 7.1-rc6 veröffentlicht: Kernel-Entwicklung stabilisiert sich nach KI-bedin
  3. Linux-Kernel CVE-2026-23111: Ein-Zeichen-Fehler in nf_tables ermöglicht lokale R