rendoc
← Back to blog

HTML to PDF: Self-Hosted vs API (2026)

7 min readrendoc team
comparisonhtml-to-pdfpuppeteer

Converting HTML to PDF sounds like a solved problem. It is not. Every approach comes with meaningful trade-offs in rendering quality, resource consumption, operational complexity, and cost. In 2026, the landscape has matured enough that you can make an informed decision based on your actual constraints instead of guessing.

This article compares the two main approaches: self-hosted solutions (running your own rendering infrastructure) and cloud PDF APIs (letting someone else handle it). We will look at real numbers, not marketing claims.

The Self-Hosted Options

Puppeteer (Headless Chrome)

Puppeteer launches a headless Chrome browser, navigates to your HTML, and calls the built-in PDF export. It is the most popular self-hosted option because Chrome rendering is the gold standard.

import puppeteer from "puppeteer";

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(htmlString);
const pdf = await page.pdf({ format: "A4", printBackground: true });
await browser.close();

Performance profile:

  • Cold start: 1-3 seconds (browser launch).
  • Warm render (reusing browser instance): 200-800 ms depending on document complexity.
  • Memory per instance: 100-300 MB. A server generating 50 concurrent PDFs needs 5-15 GB of RAM just for Chromium.

Operational reality:

  • You need Chromium binaries in your Docker image, which adds 400+ MB to your container size. Alpine-based images require additional dependencies that are easy to misconfigure.
  • Browser processes leak memory over time. Production systems need periodic restarts or process recycling.
  • Font rendering differs across Linux distributions. A PDF that looks correct on your Mac will look different in your Docker container unless you explicitly install and configure the same fonts.
  • Serverless deployment is possible but expensive. Each Lambda/Cloud Run invocation pays for Chromium startup time.

Playwright

Playwright is Puppeteer's spiritual successor, maintained by Microsoft. It supports Chrome, Firefox, and WebKit. The PDF generation API is nearly identical to Puppeteer.

The main advantage over Puppeteer is better cross-browser support and more active maintenance. The resource requirements are similar: you are still running a full browser engine.

wkhtmltopdf

wkhtmltopdf uses the QtWebKit engine, which is a fork of WebKit from around 2015. It is significantly lighter than Chromium (50-80 MB RAM per render) and faster for simple documents.

The catch: it does not support modern CSS. Flexbox renders incorrectly. CSS Grid does not work. CSS custom properties are ignored. If your HTML uses anything beyond CSS2-era layouts, wkhtmltopdf will produce broken output.

The project has been effectively abandoned. The last release was in 2020. Using it in 2026 means relying on deprecated software with known security vulnerabilities.

Gotenberg

Gotenberg wraps Chromium and LibreOffice in a Docker container with a REST API. It is essentially "self-hosted Puppeteer as a service" with a clean HTTP interface.

curl -X POST http://localhost:3000/forms/chromium/convert/html \
  -F "files=@index.html" \
  --output result.pdf

Gotenberg solves the integration problem (you get an HTTP API instead of managing browser instances in your application code) but not the infrastructure problem. You still run Chromium, still manage memory, and still handle scaling.

The Cloud API Option

Cloud PDF APIs abstract away the entire rendering pipeline. You send HTML or template data via HTTP; you receive a PDF back. The provider handles Chromium, fonts, scaling, and infrastructure.

const response = await fetch("https://api.rendoc.dev/v1/render", {
  method: "POST",
  headers: {
    "Authorization": "Bearer your_api_key",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    html: htmlString,
    options: {
      format: "A4",
      margin: { top: "20mm", bottom: "20mm" },
    },
  }),
});

const pdf = await response.arrayBuffer();

Performance profile:

  • Latency: 500 ms - 2 seconds depending on document complexity and provider.
  • No cold starts from your perspective. The provider maintains warm browser pools.
  • Memory usage on your servers: effectively zero. You are making an HTTP call.

Honest Comparison

FactorPuppeteer/PlaywrightwkhtmltopdfGotenbergCloud API
CSS supportFull modern CSSCSS2 onlyFull modern CSSFull modern CSS
Server RAM (per render)100-300 MB50-80 MB100-300 MB0 MB
Render latency200-800 ms warm100-400 ms300-900 ms500 ms - 2s
Docker image size+400 MB+80 MB~1.5 GB0 MB
Ops complexityHighMediumMediumNone
Cost at 1K docs/mo$5-20 server$5-10 server$5-20 server$0-10 API
Cost at 100K docs/mo$100-400 server$50-150 server$100-400 server$50-200 API
Data privacyFull controlFull controlFull controlLeaves your network

When Self-Hosted Wins

Self-hosting makes sense in specific scenarios:

  • Regulatory requirements. If your data cannot leave your network (healthcare, government, finance), self-hosted is the only option. Some API providers offer on-premise deployments, but verify this before committing.
  • Extreme latency sensitivity. If you need sub-200ms render times consistently, a warm Puppeteer instance on the same network will beat any cloud API.
  • Very high volume with predictable load. At 500K+ documents per month with steady traffic, dedicated servers can be cheaper than API pricing. But you pay with engineering time for maintenance.
  • You already have the infrastructure. If your team already manages Chromium in production for other reasons (screenshots, testing), adding PDF generation is incremental.

When a Cloud API Wins

The API approach wins in more common scenarios:

  • Small to medium teams. Every hour spent debugging font rendering in Docker is an hour not spent on your product. For teams under 20 engineers, the infrastructure tax of self-hosted Chromium is disproportionate.
  • Variable or unpredictable volume. If you generate 500 invoices on the first of the month and 10 the rest of the time, paying per document is significantly cheaper than maintaining servers sized for peak load.
  • Serverless architectures. If your application runs on Vercel, Netlify, or AWS Lambda, bolting on a Chromium instance defeats the purpose. An API call fits naturally.
  • Fast iteration. Cloud APIs with template management let designers update PDF layouts without code changes or deployments.

The Hidden Costs of Self-Hosting

Most teams underestimate the ongoing cost of running Puppeteer in production. Here is what actually consumes time beyond the initial setup:

  • Chromium updates. Security patches for the browser engine need to be applied regularly. Each update can change rendering behavior slightly, requiring visual regression testing of your templates.
  • Font management. Installing fonts in Docker containers, ensuring font licenses allow server-side usage, handling fallback fonts for Unicode characters your users might input.
  • Memory leak debugging. Chromium processes are notorious for memory leaks in long-running scenarios. You will eventually write a process recycler.
  • Scaling logic. Browser pools, connection queuing, timeout handling, and graceful degradation under load. This is not application code but it needs to be maintained.

A Practical Middle Ground

Many teams start with Puppeteer because it is free and they can get a proof of concept working in an afternoon. That is reasonable. The question is when the operational overhead exceeds the cost of an API.

A practical approach: start with a cloud API for development and production. If you reach a volume where the API cost exceeds what you would pay for dedicated infrastructure plus engineering time to maintain it, migrate to self-hosted. Most teams never reach that crossover point.

rendoc is designed for this pragmatic approach. The free tier covers prototyping and low-volume production. The API is straightforward enough that migrating to it from Puppeteer means replacing your render function with an HTTP call. And if you ever outgrow the API, your HTML templates work with any rendering engine because there is no vendor lock-in.

Making the Decision

Ask yourself three questions:

  1. Does your data need to stay on your network? If yes, self-host. If no, both options are on the table.
  2. How many PDFs per month, and how predictable is the volume? Predictable high volume favors self-hosted. Variable or growing volume favors API.
  3. What is your team's time worth? If an engineer spends 4 hours per month maintaining Chromium infrastructure at a loaded cost of $100/hour, that is $400/month in hidden cost. Compare that to your API bill.

There is no universally correct answer. But for most teams building SaaS products in 2026, the cloud API approach delivers better value per engineering hour invested. The self-hosted path is valid when you have specific constraints that require it.

Generate PDFs without the headaches

rendoc turns your templates into pixel-perfect PDFs with a single API call. Free tier included.