Gotenberg: a Docker API that turns HTML, Office files and URLs into PDFs
A developer-friendly API for converting many document formats into PDF files, and more!
At a glance
- What is it?
- Gotenberg bundles Headless Chromium and LibreOffice behind one HTTP endpoint, so document conversion becomes a POST request instead of a rendering stack you maintain. It is a good fit for backends that already run containers, and a poor fit if you need a native library or fine control over the browser.
- Who is it for?
- Adopt Gotenberg if your stack already runs containers and you want HTML-to-PDF, Office conversion and PDF manipulation behind one HTTP endpoint with basic auth, TLS and OpenTelemetry hooks available as flags. Do not adopt it if you need a native library inside your own process, or if you must control Chromium's rendering pipeline directly; the go.mod pins chromedp at v0.14.2 precisely because v0.15.x breaks print mode.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The conversion stack Gotenberg replaces
Producing a PDF from arbitrary input normally means assembling several unrelated tools. HTML needs a headless browser with fonts installed. A .docx needs LibreOffice in headless mode. Merging, rotating or encrypting the result needs a PDF toolkit. Each piece has its own process model, its own failure modes and its own set of system packages to keep patched.
Gotenberg's claim is that all of this fits behind one HTTP service. The README describes it as "a Docker-based API for converting documents to PDF" and says you send files via multipart/form-data and get a PDF back, with no need to manage Chromium, LibreOffice or fonts yourself. The audience is backend and platform engineers who need conversion as a step in a pipeline rather than as a desktop tool.
The scope is wider than the tagline suggests. The feature list covers HTML, URL and Markdown to PDF through Headless Chromium; Office documents through LibreOffice, which the README puts at 100+ formats; PDF merging, splitting, rotation and flattening; watermarking, stamping and encryption; PDF/A and PDF/UA compliance; screenshots of URLs and HTML; and reading or writing metadata and bookmarks. That is closer to a document service than a single converter.
One HTTP port in front of Chromium, LibreOffice and a PDF toolkit
The architecture follows from the packaging. Gotenberg is a Go program, module github.com/gotenberg/gotenberg/v8, that exposes an HTTP API and shells out to the conversion engines installed in the same image. Routing is handled by labstack/echo v5, and the dependency list shows chromedp for browser control, gomarkdown/markdown for Markdown input, and mholt/archives for archive handling. There is no separate worker tier: the API process and the converters live in one container.
That single-process design explains the operational flags. CHROMIUM_RESTART_AFTER defaults to 100 in the .env file, so the browser is recycled after a set number of conversions rather than running indefinitely. CHROMIUM_MAX_QUEUE_SIZE and CHROMIUM_IDLE_SHUTDOWN_TIMEOUT are also configurable, which tells you Chromium instances are pooled and queued rather than spawned per request. Under load, conversion capacity is therefore a function of how many browser instances you allow and how long each one stays alive.
The request path is short. A client POSTs multipart/form-data to a route such as /forms/chromium/convert/url, the handler validates the form fields, hands the work to the relevant engine, and streams the resulting PDF back in the response body. API_CORRELATION_ID_HEADER defaults to Gotenberg-Trace, so a correlation identifier can be threaded through logs and traces. Telemetry is wired through OpenTelemetry, with separate exporters for traces, metrics and logs configured by environment variables in compose.yaml.
Installing Gotenberg with Docker and converting your first URL
The README gives a single command for a local instance. It publishes the container's port 3000 to the host and pulls the version 8 image tag.
docker run --rm -p 3000:3000 gotenberg/gotenberg:8Once the container is running, the API is reachable at http://localhost:3000. The README's first real example converts a URL to PDF by posting the url form field to the Chromium conversion route and writing the response body to a file.
curl \
--request POST http://localhost:3000/forms/chromium/convert/url \
--form url=https://sparksuite.github.io/simple-html-invoice-template/ \
-o invoice.pdfIf the request succeeds, invoice.pdf contains the rendered page. If it fails, the response is not a PDF, so check the status code and body rather than assuming the output file is valid.
For anything beyond a local test, the repository ships a compose.yaml that reads its configuration from environment variables. The service maps ${API_PORT} to the same port inside the container and passes credentials through GOTENBERG_API_BASIC_AUTH_USERNAME and GOTENBERG_API_BASIC_AUTH_PASSWORD, with basic auth toggled by the --api-enable-basic-auth flag.
services:
gotenberg:
image: ${DOCKER_REGISTRY}/${DOCKER_REPOSITORY}:${GOTENBERG_VERSION}
ports:
- "${API_PORT}:${API_PORT}"
environment:
GOTENBERG_API_BASIC_AUTH_USERNAME: ${GOTENBERG_API_BASIC_AUTH_USERNAME}
GOTENBERG_API_BASIC_AUTH_PASSWORD: ${GOTENBERG_API_BASIC_AUTH_PASSWORD}The Makefile's .env block is the reference for defaults: API_PORT=3000, API_START_TIMEOUT=30s, API_TIMEOUT=30s, API_ROOT_PATH=/, API_ENABLE_BASIC_AUTH=false and API_ENABLE_OIDC_AUTH=false. The Makefile also carries a build target that accepts TARGET=gotenberg-chromium or TARGET=gotenberg-libreoffice, which implies the image can be built in variants that omit one of the two engines.
The download-from feature is where deployments get exposed
Gotenberg can fetch remote resources on your behalf, and that is the part of the configuration most likely to cause trouble. The .env file is explicit about it: API_DOWNLOAD_FROM_DENY_LIST is empty by default, matching the flag default since 8.32.0, and the comment says a textual deny-list cannot enumerate every way to write a private address, so API_DOWNLOAD_FROM_DENY_PRIVATE_IPS is the control to reach for. In the shipped defaults that flag is false, left that way so local testing can reach the host.
Shipping that default false is a defensible choice for a project whose first experience is a local docker run, but it means a deployment that copies the .env without reading the comment inherits a service that can be pointed at internal addresses. The controls exist: API_DOWNLOAD_FROM_ALLOW_LIST, API_DOWNLOAD_FROM_DENY_PUBLIC_IPS, API_DISABLE_DOWNLOAD_FROM, plus retry and concurrency limits via API_DOWNLOAD_FROM_MAX_RETRY (4) and API_DOWNLOAD_FROM_MAX_CONCURRENCY (10). None of them are on by default. If your Gotenberg instance accepts URLs from untrusted users, this is the first thing to review.
The second limitation is the engine pin. The go.mod carries an explicit note that chromedp is pinned at v0.14.2 because v0.15.x "breaks the headless print-mode paint pipeline (rAF / ResizeObserver / IntersectionObserver stop firing, blank charts)". That is a real constraint on upgrades: the browser automation layer cannot simply track upstream releases, and any chart or lazy-rendered content in your HTML depends on that pin holding.
Finally, Gotenberg is the wrong tool when you need conversion inside your own process. It is a service by design, so embedding it means running a container alongside your application and paying the network hop for every document.
Gotenberg versus calling Puppeteer or LibreOffice yourself
The obvious alternative for HTML-to-PDF is Puppeteer, or chromedp if you are writing Go. Both give you direct control over the browser: you choose the Chrome build, set the print options, inject scripts before rendering, and decide when the page context is torn down. Gotenberg wraps that same class of engine but exposes a fixed set of form fields instead of a scripting API. You gain a stable HTTP contract and lose the ability to intervene mid-render.
On the Office side, the alternative is running LibreOffice headless yourself, which is what Gotenberg does internally. The difference is packaging: you would install LibreOffice, manage its profile directories, and handle the cases where a conversion hangs. Gotenberg's contribution there is the queueing and restart behaviour around the process, not a different conversion engine.
A third approach is a pure-library converter such as WeasyPrint, which renders HTML without a browser engine. That avoids the Chromium process entirely and is far lighter, but it implements its own CSS layout rather than using a browser's, so pages that depend on browser-specific behaviour will not match. Gotenberg is the better choice when fidelity to a real browser matters more than process weight.
Maintenance, licensing and what an upgrade actually costs
The repository is not archived, and the last push was on 2026-09-18. Releases are frequent: v8.37.0 on 2026-09-11, v8.36.0 on 2026-08-14 and v8.35.0 on 2026-08-07. The Go module path carries a v8 major version, so upgrades within the 8.x line are the expected path.
The licence is MIT. That is permissive: you can use Gotenberg commercially, modify it and redistribute it, provided the copyright notice and permission notice are preserved. It does not impose copyleft obligations on your own code, and it does not come with a warranty. This is a description of the licence text, not legal advice; if your organisation has specific compliance requirements, review the LICENSE file in the repository.
Upgrade cost is dominated by two things. First, the chromedp pin documented in go.mod means the browser automation dependency moves deliberately, not automatically, so a routine dependency bump is not routine here. Second, the image bundles Chromium and LibreOffice, so image size and the surface you must patch track those upstream projects rather than Gotenberg's own release cadence. Running a specific tag rather than a floating one is the practical way to keep a known-good engine combination.
Editorial conclusion
Adopt Gotenberg if your stack already runs containers and you want HTML-to-PDF, Office conversion and PDF manipulation behind one HTTP endpoint with basic auth, TLS and OpenTelemetry hooks available as flags. Do not adopt it if you need a native library inside your own process, or if you must control Chromium's rendering pipeline directly; the go.mod pins chromedp at v0.14.2 precisely because v0.15.x breaks print mode. Before rolling it out, verify two things on your own hardware: that your Office and HTML fixtures render as expected, and that API_DOWNLOAD_FROM_DENY_PRIVATE_IPS is set the way your network requires, since the Makefile ships it as false for local testing.
Frequently asked questions
What is Gotenberg used for?
It converts documents to PDF over HTTP. The README lists HTML, URL and Markdown to PDF via Headless Chromium, Office documents via LibreOffice, plus PDF merging, splitting, rotation, flattening, watermarking, stamping, encryption, screenshots, and metadata or bookmark editing.
Is Gotenberg free to use?
Yes. The repository is licensed under MIT, which permits commercial use, modification and redistribution as long as the copyright and permission notices are kept. The project also accepts sponsorship, but that is optional.
Can Gotenberg convert HTML to PDF?
Yes. HTML, URL and Markdown to PDF are handled by Headless Chromium, and the README's first example posts a url form field to /forms/chromium/convert/url and writes the response to invoice.pdf.
What does Gotenberg do?
It runs as a Docker-based API that accepts files via multipart/form-data and returns PDFs, covering browser-based HTML rendering, LibreOffice conversion and PDF manipulation in one service.
How does Gotenberg compare with Puppeteer?
Both drive a headless browser, but Gotenberg exposes a fixed HTTP API with form fields rather than a scripting interface. You get a stable contract and lose the ability to inject scripts or control the page context during rendering.
What alternatives to Gotenberg exist?
Calling Puppeteer or chromedp directly gives you control over the browser build and print options, running LibreOffice headless yourself removes the API layer, and a pure-library converter such as WeasyPrint avoids the Chromium process but uses its own CSS layout engine.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/gotenberg-gotenberg)