Gotenberg
API for converting documents to PDF
Gotenberg is a self-hosted, Docker-powered API for converting many document formats and HTML into PDF. It is often used as a backend service for document generation and archiving pipelines.
Key features
- HTML and Office to PDF
- Stateless REST API
- Merge and convert documents
- Easy Docker deployment
Pros & cons
Strengths
- Simple powerful conversion API
- Easy to integrate
Trade-offs
- Not a document archive
- API-only, no UI
Gotenberg replaces
Last reviewed Aug 26, 2026 · 850 words
Gotenberg is not a PDF tool you open. It is a stateless HTTP service that other software posts files to and gets PDFs back from, and the most common reason a self-hoster runs it is that Paperless-ngx needs it to consume Word, Excel and email attachments. If that is you, the whole job is 8 lines of compose and 2 command flags. If you want to generate invoices or reports from HTML in your own scripts, it is still the best free option I know, with one warning: it wraps a full Chromium and a full LibreOffice, so the 512 MB listed as the minimum is exactly that, a minimum.
Where it sits in a stack
Gotenberg does one thing: convert. It holds no documents, has no database, no users and no UI; the "API-only" line in most listings is the design, not an omission. You POST a multipart form to a route, it returns a PDF, and it forgets everything. That makes it trivially replaceable and trivially scalable (run 3 copies behind a load balancer if a batch job needs it), and it means backups are a non-topic. What consumes the output is your problem: Paperless-ngx for archiving, a script for invoice generation, or Stirling-PDF if what you actually want is a human-facing toolbox for splitting, merging and OCR. Stirling and Gotenberg are not rivals; one is a workbench, the other is a pipe.
The compose Paperless-ngx expects
services:
gotenberg:
image: gotenberg/gotenberg:8
restart: unless-stopped
command:
- "gotenberg"
- "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*"
Two lines carry the weight. Disabling JavaScript stops a hostile HTML email from running script inside your converter, and the allow-list restricts what Chromium may load to the temp files Gotenberg itself wrote, so a document cannot pull resources off your LAN. Paperless then needs PAPERLESS_TIKA_ENABLED=1, PAPERLESS_TIKA_GOTENBERG_ENDPOINT=http://gotenberg:3000 and a Tika container alongside; the Paperless-ngx guide covers that pairing. Note there is no ports: block. Gotenberg listens on 3000 inside the Docker network and nothing outside needs to reach it.
Three routes cover almost every job
HTML to PDF, the route I use most:
curl -s -X POST http://localhost:3000/forms/chromium/convert/html \
-F [email protected] -F [email protected] \
-o invoice.pdf
The main file must be called index.html; extra files (CSS, images, fonts) go in as further files fields and are referenced by relative path. Office to PDF is /forms/libreoffice/convert with a .docx, .xlsx, .pptx or .odt attached, and it handles the ugly ones (tracked changes, embedded fonts) about as well as LibreOffice on a desktop does, which is to say well for text and unpredictably for complex layout. Merging is /forms/pdfengines/merge with several PDFs; they are merged in alphabetical order of filename, so name them 01-, 02- and so on. Page size, margins and orientation are form fields on the Chromium route (paperWidth, paperHeight, marginTop and friends, in inches).
Long jobs can be made asynchronous: send a Gotenberg-Webhook-Url header and the service POSTs the finished PDF to that URL instead of holding your connection open. For a nightly batch of 500 statements that is the pattern; for a single invoice it is overkill.
Give it more RAM than the minimum
A single Chromium tab rendering a 40-page HTML report takes 300 to 600 MB on its own; LibreOffice converting a spreadsheet with charts is similar. On a 512 MB limit you will see conversions succeed alone and fail when two arrive together. I set a 1 GB memory limit for a personal Paperless setup and 2 GB where anything generates PDFs in parallel, and I raise --api-timeout to 60s (the default is 30s) because a large scanned PDF through LibreOffice really can take that long on a small ARM box. CPU matters less than RAM; a 2-core VPS is fine.
Do not expose it
Gotenberg has no authentication of its own. Anyone who can reach port 3000 can make it fetch arbitrary URLs through the /forms/chromium/convert/url route and burn your CPU. Keep it on an internal Docker network, and if a remote system genuinely must call it, put it behind Caddy with basic auth or a client certificate rather than opening the port. Keep the --chromium-allow-list flag even when you are not running Paperless; the default without it is permissive.
What I'd do
Run gotenberg/gotenberg:8 with JavaScript disabled and the file allow-list set, no published port, a 1 GB memory limit, on the same compose stack as whatever calls it. Pair it with Paperless-ngx for archiving and use the Chromium HTML route from your own scripts for anything you would otherwise pay a PDF API to do. It is a 13,000-star, MIT-licensed, single-purpose service that has never once needed my attention after the first hour, which is the highest praise I have for infrastructure.
Similar document management apps
Stirling PDF
Document ManagementPowerful locally hosted web-based PDF toolkit
Replaces Adobe Acrobat, Smallpdf
Paperless-ngx
Document ManagementDocument management for scanning, indexing and archiving
Replaces Evernote, DEVONthink
OCRmyPDF
Document ManagementAdd a searchable OCR text layer to scanned PDFs
Replaces Adobe Acrobat
DocuSeal
Document ManagementOpen-source document signing and e-signature platform
Replaces DocuSign, HelloSign
BentoPDF
Document ManagementPowerful, privacy-first, client-side PDF toolkit that allows you
Replaces Adobe Acrobat
Documenso
Document ManagementThe open-source DocuSign alternative
Replaces DocuSign, Adobe Sign