Repository navigation
Gotenberg 9 is out 🎉
Important
latest now points to Gotenberg 9. Pin gotenberg/gotenberg:8 until you have upgraded. The upgrade guide maps every Gotenberg 8 route, field, flag, metric and span to its Gotenberg 9 counterpart.
One PDF engine
pdfcpu, QPDF, pdftk and ExifTool are replaced by litho, a PDF engine written in Go for Gotenberg, running in-process.
remediate repairs a PDF until it conforms to one of the eleven PDF/A levels, to PDF/UA-1 or PDF/UA-2, to both together, or to Factur-X, then checks its own result: Gotenberg-Remediation-Residual counts the violations left, Gotenberg-Remediation-Rolled-Back the fixes undone to keep pages, text and images. /forms/pdf/validate runs the same checks on any PDF.
litho also signs and timestamps, redacts from the file rather than from view, infers a structure tree, renders pages as images, fills and flattens forms in the scripts the image's fonts cover, and shrinks a PDF without touching image quality. A stage that would drop the signatures of a signed PDF answers 400, as does text no font can draw.
Stages, in your order
Post-processing is no longer flat fields in a fixed order. A stages list runs as written, on every route that makes a PDF and on /forms/pdf/pipe for PDFs you already have:
[{"op": "merge"}, {"op": "stamp-text", "text": "DRAFT"}, {"op": "remediate", "profile": "PDF/A-2b"}]26 stages, each also a /forms/pdf/<op> route with form fields, plus six JSON read routes: info, validation, metadata, bookmarks, attachments and form fields. A broken order answers 400 before anything runs.
LibreOffice through lok
unoconverter, Python and the soffice listener are replaced by lok, a long-running LibreOfficeKit process that Gotenberg supervises and restarts after a crash. Macros stay off; --libreoffice-enable-macros runs them.
Fewer dependencies, less CVE surface
Java, Python, unoconverter, pdfcpu, QPDF, pdftk and ExifTool leave the image: fewer packages to patch, and no more CVE reports about tools Gotenberg does not ship. Variants, architectures, user, port and command don't change.
Upgrading
Start from the upgrade guide. /forms/pdfengines routes become /forms/pdf routes, post-processing fields become stages, and a field with a stage twin now starts with native. A removed route answers 404 naming its replacement, a Gotenberg 8 field 400 saying where it went:
Invalid form data: form field 'merge' is unknown to this route; Gotenberg 9 moved it to a stage: add {"op":"merge"} to 'stages'
Custom builds import github.com/gotenberg/gotenberg/v9. The PdfEngine, PdfEngineProvider and MetricsProvider interfaces and the Prometheus module are gone: OTEL_METRICS_EXPORTER=prometheus serves the same.
Everything else
set-formselects several list items, refuses a value a field cannot hold, and lists the PDF's fields when you name a missing one.flatten-formdraws a value without appearance over the field's own background and border.- Flattened values and text stamps beyond Western European text use an installed font, embedded and subset. Uncovered text answers
400naming the characters. optimizepacks objects into compressed streams, stores identical streams once, recompresses images losslessly and subsets fonts. Every pass runs unless turned off. An encrypted input needs adecryptorencryptstage first.mergekeeps each file's bookmarks, remapped, and names the result after the first file in alphanumeric order.renderdraws pages as PNG or JPEG at the resolution you ask.--pdf-max-stream-bytes,--pdf-max-embedded-file-bytesand--pdf-max-image-pixelsbound what litho reads.--pdf-allow-list,--pdf-deny-listand their siblings filter the timestamp authorities it calls.
LibreOffice
nativePdfatakesPDF/A-1b,PDF/A-2b,PDF/A-3borPDF/A-4;nativePdfuatakesPDF/UA-1. The PDF/A export is no longer redone after the other steps: end the stages withremediatewhen they may break conformance.initialView,magnificationandpageLayouttake names such asoutlineorfitWidth, not numbers.firstPageOnLeft, which LibreOffice ignored, andnativePdfFormatsare gone.nativeWatermarkColortakes#rrggbb,nativeWatermarkRotationdegrees counter-clockwise,0being horizontal. A plain and a tiled watermark together answer400.- CSV files split into columns.
.epubis no longer accepted. - PDFs are named after their documents,
report.docx.pdf, even for a single one.Gotenberg-Output-Filenamestill overrides.
Chromium
paperSizenames a format, for LibreOffice too:A3,A4,A5,B4,B5,Letter,LegalorTabloid.waitForWindowStatus, renamed fromwaitWindowStatus, now waits as documented.skipNetworkAlmostIdleEvent=falsenow really waits fornetworkAlmostIdle.- The page sent to the HTML and Markdown routes passes the allow and deny lists, which keep governing what it loads.
- Print settings Chromium rejects answer
400: ascaleout of range, a negative length, awaitDelaylonger than the API timeout. A wait that outlasts the request answers503naming the field and its value. failOnHttpStatusCodesjudges the main page by its frame, not its URL. Screenshotqualitygoes from 1 to 100.- DevTools messages leave the debug logs unless
--chromium-debug-protocolis set. URL queries and extra header values stay out too.
API
archivesunpacks uploaded ZIPs, bounded by--api-archive-max-entriesand--api-archive-max-ratio.- Errors say what to send: the value sent and the one wanted, a stage option's expected value instead of a Go type, the host that does not resolve.
NaNand infinities are refused. - Webhook and
downloadFromURLs must be absolutehttporhttpsURLs with a host.Gotenberg-Webhook-Events-Urlis required withGotenberg-Webhook-Urland receives errors aswebhook.errorevents: the error URL, method and lists are gone. A failed upload says what the webhook answered, or why it was unreachable. - A non-ASCII
Gotenberg-Output-Filenameis encoded per RFC 6266. Every answer carries the trace header, 404 and 405 included; the version header too, without authentication. - The banner is an aligned startup summary, one log record outside a terminal.
--api-bind-iptakes IPv6,--api-start-timeoutbounds module readiness,/debugreports the lok and litho versions. - Debug logs mask form values, extra header values and document passwords.
Telemetry
- Names follow the OpenTelemetry rules:
gotenberg.chromium.requests,gotenberg.<engine>.queue.size,chromium.pdf,webhook.send. A failed request carrieserror.type; thestatusattribute and the errors counter are gone. - Each webhook and download attempt is an HTTP client span under the whole call's span, with
server.addressand aurl.fullstripped of credentials, query and fragment. Outbound URLs lose their query in errors and logs too. - The server span stays unset on success and
4xx, and sets Error on5xxwith the status code aserror.type.OTEL_PROPAGATORSselects the propagators. Each written document is traced, its editing stages as child spans.
Security
- The 8.40.0 fixes are in: IANA non-globally-reachable ranges are private for the
*_DENY_PRIVATE_IPSchecks, Chromium distrusts the compromised eMudhra roots from the first request, and the pinning proxy stays on with--chromium-host-resolver-rules. - Pinning proxy tunnels are bound to the server that accepted them. Refused outbound requests are logged with their context, redacted URL and rule.
- Engine temporary files are collected by exact pattern, Chromium's profile directory is removed after a failed start, and handler panics answer
500at once.
Removed
- The deprecated
--log-format,--log-enable-gcp-fields,--api-trace-header,--api-disable-health-check-logging,--chromium-incognito,--webhook-error-allow-listand--webhook-error-deny-listflags, and the--pdfengines-*and--prometheus-*ones. UNOCONVERTER_BIN_PATH, replaced byLOK_BIN_PATH, and the pdfcpu, QPDF, pdftk and ExifTool paths.downloadFrom[].embedded,facturxVersionand the merge bookmark options: the guide names their replacements.
Chore
- Debian 13, Chromium 156 (146 on ppc64le), LibreOffice 26.8 from Debian's backports, Go 1.27, lok v0.0.6, litho v0.0.7.
- CI runs the integration suite on every push to
mainand tests the Chromium and LibreOffice variants. - The Bruno collection mirrors every route.