DEV Community

IronSoftware
IronSoftware

Posted on

Puppeteer in 2026: Thirty System Libraries to Make One PDF

Here is the part of a Puppeteer PDF service that has nothing to do with PDFs.

# System libraries a headless Chromium needs before Puppeteer can boot
RUN apt-get update && apt-get install -y \
    libnss3 libatk-bridge2.0-0 libcups2 libdrm2 libgbm1 \
    libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 \
    libasound2 libpango-1.0-0 libcairo2 fonts-liberation \
    && rm -rf /var/lib/apt/lists/*
Enter fullscreen mode Exit fullscreen mode

Thirty-odd system libraries, and not one of them is about the document. They are what a headless Chromium needs to boot, and they are in the image because the PDF is being produced by a browser-automation tool rather than a PDF library. Puppeteer renders through a current Chromium, so a page.pdf() call reproduces the page as the browser draws it, which is why Node teams reach for it.

IronPDF for Node.js renders HTML through the same class of engine but ships as a PDF library, so the block above collapses to an npm install and the document work stays in one package. That difference in shape is what this piece is about.

Full disclosure. We build IronPDF for Node.js at Iron Software, and this read looks at where Puppeteer's browser-automation shape costs a PDF service and where IronPDF covers the same job from one package.

What Does Running a Browser Actually Cost?

The cost is not the page.pdf() call, which works. It is everything the call drags in behind it. Puppeteer is a browser-automation library, so making a PDF is one method on a tool built for navigation, network interception, and DOM scripting, and a PDF service ships and patches all of that machinery to use a single call.

The first cost is manipulation. Puppeteer cannot merge, split, stamp, or sign after it renders, so each of those is a second dependency and a second object model bolted on afterward. The second is the container. Puppeteer's headless Chromium needs that set of shared libraries present in the image, and with no official Alpine build as of 2026, a missing libnss3 or font package breaks the render at runtime rather than failing the build, so the Dockerfile gets tuned by failed deploy. The third is time. Puppeteer's bundled browser is a moving target a team patches on Chromium's security cadence, because a PDF service built this way ships a full browser it did not need and inherits its vulnerability surface as a standing tax.

None of the three is the PDF. They are the tax on making one with a browser.

One Package Built for the PDF

IronPDF renders the same HTML and then keeps going, because manipulating the document afterward is part of the same library rather than the next dependency.

import { PdfDocument } from "@ironsoftware/ironpdf";

// Render HTML to a PDF
const invoice = await PdfDocument.fromHtml(
  "<h1>Invoice #1042</h1><p>Total due: $250.00</p>"
);

// Then keep working on it, same package, no second library
const terms = await PdfDocument.fromHtml("<h2>Terms and Conditions</h2>");
const merged = await PdfDocument.mergeAsync([invoice, terms]);

await merged.saveAs("invoice.pdf");
Enter fullscreen mode Exit fullscreen mode

That renders a document and merges another onto it from one import, with no browser to provision in the image and no separate PDF toolkit for the second half. You can pull IronPDF for Node.js from npm and run this render-and-merge path in a couple of minutes, and the HTML-to-PDF guide walks the generation side end to end.

How Do the Two Compare, Line by Line?

The gap is smallest at the render call and widest everywhere around it.

Capability Puppeteer IronPDF for Node.js
Built for Browser automation, PDF is one method PDF generation and editing
HTML and CSS to PDF Yes, via page.pdf() Yes, PdfDocument.fromHtml
Merge, split, stamp, sign Not supported, add a second library Supported, PdfDocument.mergeAsync
Container dependencies ~30 system libraries, no official Alpine Handled by the package install
Browser patching Your team, on Chromium's cadence Handled inside the package
Header, footer, page numbering Hand-built in HTML per document Built-in options on the render call

Table 1. Puppeteer and IronPDF for Node.js across the generation and post-render questions, from each project's own documentation.

Where IronPDF Skips the Browser

The one case for Puppeteer is a service already running it for scraping or end-to-end testing, where a page.pdf() call is one more use of a browser the image already carries and patches. That is rarely why Puppeteer lands in a PDF pipeline, though, since most teams that reach for it here install it for the PDF alone and take on the whole browser, its libraries, and its patch schedule to get one method.

On that far more common side, the choice is between provisioning a headless browser yourself or generating and editing the PDF with IronPDF for Node.js from one package, and the guide on HTML to PDF without Puppeteer walks that path end to end.

IronPDF for Node.js has a free trial if you want to run your own HTML through it before deciding.

Which describes your service, a browser you were already running for other reasons, or one you installed only to make PDFs? Tell us in the comments, we are curious how many started with the second and inherited the first.

Puppeteer is an open-source project maintained by the Chrome team, distributed under Apache-2.0, and is not affiliated with Iron Software. The details above are drawn from the project's own documentation and container guidance. If something has changed since, correct us in the comments.

Top comments (0)