Document API vs wkhtmltopdf vs Chrome Headless: Which PDF Engine to Choose?

Published March 20, 2026 · 10 min read · Doxnex Engineering

Choosing a PDF generation engine is one of those decisions that seems straightforward until you are three months into production, debugging rendering inconsistencies at 2 AM. The landscape has changed significantly in recent years: wkhtmltopdf is deprecated, Chrome Headless has matured, and purpose-built document APIs have emerged as a serious alternative.

This article provides an honest, technical comparison of the three main approaches to server-side PDF generation. We will examine rendering quality, performance, memory consumption, container-friendliness, and long-term maintainability.

The Contenders

wkhtmltopdf

A command-line tool that uses a patched version of QtWebKit to render HTML to PDF. For years it was the default choice for open-source PDF generation. However, the project was archived and is no longer maintained. The underlying WebKit fork is frozen, meaning no new CSS features, no security patches, and growing incompatibility with modern HTML.

Chrome Headless (Puppeteer / Playwright)

Google Chrome running without a GUI, controlled via the DevTools Protocol. Libraries like Puppeteer (Node.js) and Playwright (multi-language) provide high-level APIs. This approach renders HTML exactly as Chrome would, supporting the latest CSS and JavaScript features.

Document API (JVM-native or Cloud API)

Purpose-built libraries like OpenHtmlToPdf (Java) or cloud APIs like Doxnex that handle the entire pipeline: template management, rendering, storage, and delivery. These solutions trade some CSS flexibility for dramatically better performance and operational simplicity.

Comparison Matrix

Criteria wkhtmltopdf Chrome Headless Document API
Rendering quality Outdated WebKit; CSS3 gaps Excellent; full modern CSS Good; CSS 2.1 + paged media
Speed (single doc) 500ms - 2s 1s - 3s 100ms - 300ms
Memory per render 50 - 150 MB 200 - 500 MB 20 - 50 MB
Concurrent renders Process per render Tab per render Thread per render
Docker image size 300 - 600 MB 500 MB - 1.2 GB 100 - 200 MB
JavaScript support Partial (old engine) Full None
Maintenance status Archived / EOL Active (Google) Active
Security Unpatched vulnerabilities Regular updates No browser attack surface

Performance Deep-Dive

We benchmarked all three approaches generating the same two-page invoice template with a company logo, 20 line items in a table, and two embedded fonts. Tests were run on a 4-core, 8 GB RAM machine inside Docker containers.

Throughput

The results were dramatic. The JVM-native document API generated 180 documents per second with 8 threads. Chrome Headless managed 12 documents per second with 4 concurrent browser contexts before hitting memory limits. wkhtmltopdf achieved 8 documents per second as each render spawned a new process.

Memory Under Load

Under sustained load of 50 concurrent requests, Chrome Headless peaked at 3.8 GB of RAM. The document API peaked at 380 MB. wkhtmltopdf peaked at 1.2 GB but crashed intermittently due to memory fragmentation in the QtWebKit engine.

Container-Friendliness

Modern deployments run in containers on Kubernetes or similar orchestrators. The PDF engine you choose has a massive impact on your infrastructure costs and operational complexity.

wkhtmltopdf in Docker

Requires installing X11 libraries, fonts, and the binary itself. The resulting image is bloated and fragile. Different Linux distributions produce different rendering results due to font rendering differences. Do not use for new projects.

Chrome Headless in Docker

Requires a large base image with Chrome and its dependencies. You must configure --no-sandbox (security trade-off), increase shared memory with --shm-size=2g, and install font packages. Zombie process cleanup is a common operational issue.

# Chrome Headless Dockerfile - note the complexity
FROM node:20-slim
RUN apt-get update && apt-get install -y \
    chromium fonts-liberation fonts-noto-cjk \
    libgbm1 libnss3 libatk-bridge2.0-0 \
    --no-install-recommends && rm -rf /var/lib/apt/lists/*
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
# Image size: ~900MB

Document API in Docker

A JVM-native solution needs only a JRE base image and font files. No system dependencies, no sandboxing concerns, no process management.

# Document API Dockerfile - simple and small
FROM eclipse-temurin:21-jre-alpine
COPY app.jar /app/app.jar
COPY fonts/ /app/fonts/
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
# Image size: ~180MB

When to Choose Each Option

Choose Chrome Headless When:

Choose a Document API When:

Do Not Choose wkhtmltopdf

There is no scenario where wkhtmltopdf is the right choice for a new project in 2026. If you have an existing integration, budget time for migration. The unpatched security vulnerabilities alone make it a liability.

The Doxnex Approach

Doxnex uses a JVM-native rendering engine (Thymeleaf + OpenHtmlToPdf) behind a REST API. You send a template and data, and receive a PDF. No browser processes, no heavy containers, no infrastructure headaches. It is the performance of a native library with the convenience of a cloud API.

Frequently Asked Questions

Is wkhtmltopdf still maintained?

No. wkhtmltopdf was archived and is no longer actively maintained. It relies on a patched version of QtWebKit that is years behind modern web standards. Known security vulnerabilities will not be patched. If you are starting a new project, do not choose wkhtmltopdf. If you have an existing integration, plan a migration to a maintained alternative.

How much memory does Chrome Headless use for PDF generation?

A single Chrome Headless instance typically consumes 200-500 MB of RAM, depending on page complexity. Each concurrent render requires its own browser context. For high-throughput scenarios, this means you may need several gigabytes of RAM just for PDF generation, making it impractical without significant infrastructure investment.

Can I use Chrome Headless in a Docker container?

Yes, but it requires careful configuration. Chrome needs the --no-sandbox flag in containers (which reduces security), shared memory must be increased (--shm-size=2g), and you need to install numerous system dependencies for font rendering. The resulting Docker image is typically 500MB-1GB. JVM-native solutions produce images under 200MB.

What is the fastest PDF generation approach for server-side rendering?

JVM-native libraries like OpenHtmlToPdf combined with a template engine offer the fastest server-side PDF generation, typically under 200ms per document. API-based services like Doxnex abstract this complexity and add caching, template management, and scalability on top, while maintaining sub-second response times including network overhead.