Skip to content

Gotenberg alternative: pagenull or self-hosting Gotenberg?

, 7 min read

Gotenberg is a good open-source project: a Docker image with an HTTP API that turns HTML, URLs, Markdown and office documents into PDFs, using Chromium and LibreOffice. It’s MIT-licensed, actively released (version 8.39.0 came out on 27 October 2026), and many teams run it in production. If you’re weighing it against a hosted API like pagenull, this guide tries to help you decide, including when the answer is Gotenberg.

When self-hosting Gotenberg is the right choice

  • You convert office documents, or work on existing PDFs: Word and spreadsheets through LibreOffice, merging, splitting, encrypting, watermarking. pagenull does HTML and URLs only.
  • Documents can’t leave your network: on-premises, air-gapped, or a policy that rules out any outside processor. Gotenberg runs wherever Docker runs.
  • Your volume is very high and steady. Servers you already pay for can cost less per document than any per-render price.
  • You have people to run it and want full control: which Chromium build, which flags, how it scales.

If one of these is you, pick Gotenberg. The rest of this guide is about what that choice involves, and what a hosted API takes off your plate.

What running it yourself involves

Starting it is one command (docker run --rm -p 3000:3000 gotenberg/gotenberg:8, from its quick start). Running it for real is the work:

  • Capacity. By default one instance runs 6 Chromium conversions at once, has no queue limit, and restarts Chromium after 100 conversions (configuration). You size the memory and CPU for your documents, and add instances behind a load balancer for peaks.
  • Timeouts. Requests stop after 30 s by default. A slow page or a large document needs that tuned, and your code needs to retry what fails.
  • Access. Authentication is off by default; you either keep the service off the internet or turn on basic auth or OIDC (configuration).
  • Rendering URLs or user HTML safely. By default a rendered page may load private addresses, such as your internal services or the cloud metadata endpoint. Gotenberg has a flag for it, --chromium-deny-private-ips, which covers navigations and sub-resources; you have to turn it on (configuration).
  • Updates. Chromium gets security fixes often. Following Gotenberg’s releases and redeploying is an ongoing task, not a one-off.
  • Monitoring. There’s a health route and a version route (routes); alerts, dashboards and log retention are yours to set up.
  • Backups: none needed. Gotenberg keeps no database, and the quick start runs it as a throwaway container with no volume. That’s one thing self-hosting doesn’t add.

What pagenull adds

  • No infrastructure. No servers, no Chromium upgrades, no capacity planning; one HTTPS request.
  • EU hosting. Hosted in Helsinki, Finland, with EU sub-processors only (trust).
  • Nothing stored. Your HTML and the PDF are never written to disk; every response says pagenull-retention: none (security).
  • A signed receipt for every render. An Ed25519 signature over the SHA-256 of the exact file, checkable by anyone on /verify. Useful when you later need to show what you sent and when.
  • E-invoices from JSON. /v1/invoices builds the EN 16931 XML, calculates the totals and checks the rules. Gotenberg can embed a Factur-X XML into a PDF, but producing a correct XML is up to you (its Factur-X route).
  • Templates. Save a layout once and render it with JSON (templates).
  • SSRF protection on by default, for the page and everything it loads, after redirects and DNS.
  • A countersigned DPA on the Volume plan (pricing).
  • A free tier: 100 renders a month, no card (pricing).

Side by side

Gotenberg 8.39.0, checked 2026-11-05 against its own docs and repository. Each Gotenberg cell links to where it says so.

Gotenberg (self-hosted)pagenull
What it isOpen-source software (MIT licence), a Docker image you run on your own servers sourceA hosted API; nothing to run
What it convertsHTML, URLs and Markdown through Chromium; office documents through LibreOffice sourceHTML and URLs through Chromium; no office documents
Working on existing PDFsMerge, split, encrypt, watermark, stamp, flatten, rotate, metadata and bookmarks routes sourceNot offered
AuthenticationOff by default; basic auth or OIDC when you turn it on sourceAn API key on every request
Rendered pages reaching private addressesAllowed by default; --chromium-deny-private-ips blocks navigations and sub-resources that resolve to them sourceAlways refused, for the page and everything it loads, after redirects and DNS
Request time limit30 s by default (--api-timeout) sourcetimeoutMs per request: 30 s by default, up to 60 s
Capacity6 Chromium conversions at once by default; queue limit off by default; Chromium restarts after 100 conversions sourceOur side. When every renderer is busy you get 503 busy with Retry-After
Page defaults8.5 × 11 in, 0.39 in margins, backgrounds off, CSS @page size ignored unless preferCssPageSize sourceA4, 12 mm margins, backgrounds on, a CSS @page size wins
PDF/APDF/A-1b, 2b, 3b and 4, and PDF/UA, on the Chromium routes sourcePDF/A-3b for e-invoices only
E-invoices (Factur-X / ZUGFeRD)Embeds a Factur-X XML you produce yourself into a PDF sourceBuilds the EN 16931 XML from JSON, calculates the totals, checks the rules and embeds it
Getting the result elsewhereWebhook headers send the result to a URL you give sourcedeliverTo uploads to a presigned URL in your own storage
Proof of what was producedNo route signs the output sourceAn Ed25519-signed receipt with every render, checkable on /verify

Moving from Gotenberg

A Gotenberg HTML request as you may have it now, with A4 paper, 2 cm top and bottom margins and backgrounds on:

curl --request POST http://localhost:3000/forms/chromium/convert/html \
  --form files=@index.html \
  --form paperWidth=21cm --form paperHeight=29.7cm \
  --form marginTop=2cm --form marginBottom=2cm \
  --form printBackground=true \
  -o invoice.pdf

The same document through pagenull. The HTML goes in a JSON body instead of a form upload:

curl https://api.pagenull.dev/v1/pdf \
  -H "Authorization: Bearer $PAGENULL_API_KEY" \
  -H "Content-Type: application/json" \
  -o invoice.pdf \
  --data-binary @- <<EOF
{
  "html": $(jq -Rs . < index.html),
  "format": "A4",
  "margin": { "top": "20mm", "bottom": "20mm" }
}
EOF

Option by option:

  • paperWidth / paperHeight: format ("A4", "Letter" …) or width / height.
  • marginTop and the other three: margin.top and so on, with a unit (mm, cm, in or px).
  • printBackground: on by default here, off by default in Gotenberg.
  • preferCssPageSize: on by default here, so a CSS @page size wins; off by default in Gotenberg.
  • landscape, scale: same names. nativePageRanges: pageRanges. emulatedMediaType: emulateMedia.
  • waitForExpression: waitFor, which takes a CSS selector rather than a JavaScript expression. There’s no fixed delay like waitDelay; have your page add an element or attribute when it’s ready.
  • Extra files sent next to index.html (images, CSS): load them from a public URL, or inline them as data: URLs. pagenull takes one HTML document per request.

Full option list in the API reference. While you compare output, send "test": true: test renders are watermarked and free.

Sources

Facts about other projects and rules, checked on 5 November 2026.