Replace Puppeteer PDF Generation: Fix Memory Leaks & Cut 95% RAM
Using Puppeteer or Playwright to generate PDFs is one of the most common architectural antipatterns in modern web development. While page.pdf() is easy to write in a quick local script, running headless Chromium inside production containers causes severe operational headaches.
The Four Hidden Costs of Puppeteer PDF Generation
1. Out-of-Memory (OOM) Kubernetes Crashes
Chromium does not immediately return allocated memory to the host operating system after a page is closed. Under sustained traffic or multi-page table exports, a single Chrome instance easily balloons past 1.5 GB of RAM, triggering immediate Linux OOM-killer termination of your application container.
2. Zombie Child Processes
When unexpected client timeouts or connection drops occur, Chrome child processes often remain detached in the background, consuming CPU and memory until the host pod restarts.
3. Font and Chart Race Conditions
Because Chromium renders asynchronously, developers frequently resort to fragile workarounds like await page.waitForTimeout(1000) or waitUntil: 'networkidle0' to ensure web fonts and Chart.js graphics finish rendering before snapshotting. This adds artificial seconds of delay to every user request.
4. Inability to Meet Legal E-Invoicing Standards
Chromium produces standard web-optimized PDF 1.7 files. It cannot natively generate ISO 19005-3 compliant PDF/A-3b files, nor can it embed validated XML attachments required by mandatory European invoicing laws like Factur-X, ZUGFeRD, and EN 16931.
Head-to-Head Benchmark
| Production Metric | Puppeteer (Headless Chromium) | Gotenberg (Docker Microservice) | Rubrol (Native Typst Rust) |
|---|---|---|---|
| Warm P99 Latency | 1,420 ms | 980 ms | 8.4 ms |
| Cold Start Time | ~2,500 ms | ~1,800 ms | < 1 ms |
| Idle Memory Footprint | 420 MB | 550 MB | 18 MB |
| 100 Concurrent Requests RAM | 1,850 MB (High OOM risk) | 1,200 MB | < 32 MB |
| Container Image Size | ~1.2 GB (with dependencies) | ~850 MB | < 40 MB |
| European E-Invoice Support | None (Manual CLI pipes) | None | Native PDF/A-3b + Factur-X |
How Rubrol Replaces Chromium in Node.js
Rubrol eliminates the headless browser entirely. Written in native Rust, it directly compiles document specifications using the Typst typesetting system. There is no V8 engine, no headless browser instance, and no asynchronous rendering race conditions.
Before: Bloated Puppeteer Code in Node.js
// Heavy Puppeteer execution: ~1,500ms, 450MB RAM
const puppeteer = require('puppeteer');
async function printInvoice(data) {
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']
});
const page = await browser.newPage();
await page.setContent(renderHtml(data), { waitUntil: 'networkidle0' });
const pdfBuffer = await page.pdf({ format: 'A4', printBackground: true });
await browser.close();
return pdfBuffer;
}
After: Lean Rubrol Request in Node.js
// Fast Rubrol invocation: 8.4ms, 18MB RAM
async function printInvoice(data) {
const response = await fetch('http://localhost:8080/v1/compile', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
template: 'invoice',
data: data,
compliance: 'factur-x' // Built-in PDF/A-3b + XML embedding
})
});
return Buffer.from(await response.arrayBuffer());
}
Ditch the Headless Browser Today
Test the Rubrol engine live in our browser studio. See sub-10ms rendering in real time.
Launch Studio PlaygroundFrequently Asked Questions
Can Rubrol handle dynamic charts and visual data?
Yes. Rubrol supports native vector plotting via Typst (including CeTZ) as well as direct SVG injection. Charts render as razor-sharp vector paths with zero asynchronous animation delay.
How does Rubrol deploy inside Docker and Kubernetes?
Rubrol ships as a lean, single-binary container image under 40MB. It runs as a sidecar alongside your backend application pods, communicating over local HTTP or high-throughput Unix Domain Sockets.
Does Rubrol support custom corporate fonts?
Yes. TrueType (.ttf) and OpenType (.otf) fonts are loaded directly into memory and embedded into output PDFs with full font subsetting for minimal file size.