Puppeteer vs PDF API: Performance and Cost Compared
Puppeteer works well at low volumes. At scale, the operational overhead — memory spikes, Chromium version management, browser pool complexity — starts to dominate engineering time. A managed PDF API trades that overhead for a per-request cost.
This article compares the two approaches across dimensions that matter for production systems: performance, memory, concurrency, cost, and operational complexity. If you have already decided to migrate and want step-by-step code, see the Puppeteer to PDF API migration guide.
What Each Approach Actually Involves
Self-Hosted Puppeteer
Puppeteer is a Node.js library that controls a headless Chromium instance. To use it for PDF generation in production you need to:
- Install and pin a Chromium version (and update it when security patches ship)
- Build and maintain a browser pool to avoid per-request cold starts
- Handle memory limits, zombie processes, and graceful restarts
- Adjust Docker images to include Chromium (adds ~300MB)
- Manage CI/CD breakage when Chromium download URLs change
None of these tasks are related to generating PDFs. They are the cost of running a browser in production.
Managed PDF API
A managed PDF API (such as FUNBREW PDF) accepts an HTML payload over HTTP and returns PDF bytes. There is no Chromium on your servers. Scaling, engine upgrades, and concurrency are all handled by the service.
The decision comes down to whether the engineering time saved by offloading Chromium operations outweighs the per-request cost of the API.
Performance Comparison
Response Time
| Scenario | Puppeteer Cold Start | Puppeteer Warm Pool | FUNBREW PDF API |
|---|---|---|---|
| Simple HTML (~10 KB) | 3,000–5,000 ms | 500–800 ms | 300–600 ms |
| HTML with images (~100 KB) | 4,000–8,000 ms | 800–1,500 ms | 500–1,200 ms |
| Complex CSS and images | 6,000–15,000 ms | 1,500–3,000 ms | 800–2,000 ms |
| AWS Lambda (serverless) | 8,000–15,000 ms | — | 300–600 ms |
Cold start figures include Chromium boot time. A warm browser pool is competitive for simple documents, but falls behind on complex templates where page rendering takes longer than the API network round trip.
In serverless environments (Lambda, Vercel, Cloudflare Workers), Puppeteer cold starts are especially painful because Chromium must boot inside a sandbox on every invocation. The managed API adds no cold-start penalty on your side. For Lambda-specific deployment, see the serverless PDF API guide.
Memory Usage
Puppeteer (browser pool, 3 processes):
Idle: 600 MB – 1.5 GB
During PDF gen: 1 GB – 2 GB+
Peak (10 concurrent): 3 GB+
Managed PDF API (HTTP client only):
Idle: < 10 MB
During PDF gen: < 50 MB (buffering HTTP response)
Peak (100 concurrent): similar — no scaling needed on your server
On a t3.medium (4 GB RAM), a Puppeteer browser pool with three processes leaves roughly 1–2 GB for the rest of your application. Adding concurrency requires larger instances or a separate PDF service.
Docker Image Size
# Puppeteer image
FROM node:20
RUN apt-get install -y chromium # +300 MB
RUN npm install puppeteer # +200 MB
# Total: 1 GB+
# API client image
FROM node:20-alpine
# No Chromium required
# Total: ~150 MB
A smaller image means faster CI builds, faster container startup, and lower registry storage costs. For container deployment patterns, see the Docker and Kubernetes PDF API guide.
Concurrency
Puppeteer (2 GB server, browser pool of 3):
Max safe concurrency: 3–5 jobs
Beyond that: queue or OOM
Managed PDF API:
Max concurrency: effectively unlimited (rate-limit dependent)
Scaling: handled by the API provider
If you need to generate 50 PDFs simultaneously, Puppeteer requires either a very large instance or a distributed system. The managed API handles this at the service level.
Cost Comparison
Self-Hosted Costs
Infrastructure
AWS t3.medium (2 vCPU / 4 GB RAM):
On-demand: ~$33/month
Reserved: ~$20/month
Minimum to run a 3-process Puppeteer pool with headroom for the rest of your app.
Burst traffic often requires a larger instance or auto-scaling.
Engineering and Maintenance
Initial implementation (browser pool, timeouts, retries): 8–16 hours
Chromium version bumps (3–4 per year at 2–4 hours each): 6–16 hours/year
Incident response (OOM, zombie processes, ~1/month): 2–8 hours/incident
Estimated annual maintenance: 20–50 engineer-hours
At a $100/hour blended rate, 30 engineer-hours per year is $3,000 in maintenance cost alone — before counting the productivity drag of context-switching to debug Chromium crashes.
CI/CD
Chromium downloads (100–200 MB) slow every build. Docker images over 1 GB increase push/pull times and registry storage. These are small individually but compound across a large team.
Managed API Costs
FUNBREW PDF free tier: 30 PDFs/month at no cost
Paid plans: usage-based; see /pricing
Engineering maintenance: near zero (only API client changes)
Where the Break-Even Falls
Low volume (< 100 PDFs/month):
Self-hosted: server + engineer hours >> API cost
→ API wins clearly
Medium volume (100–10,000 PDFs/month):
Self-hosted: $30–100 server + 20–50 hours maintenance
API: usage-based cost
→ When maintenance hours are included, API is usually cheaper
High volume (10,000+ PDFs/month):
Self-hosted: dedicated infrastructure, dedicated ops time
API: scales on the provider's side
→ Depends on your volume and team size, but the managed provider's scale
efficiency is hard to match
The cost most teams undercount is engineer opportunity cost. Every hour spent managing Chromium is an hour not spent on the product.
Operations Comparison
| Dimension | Puppeteer (self-hosted) | Managed PDF API |
|---|---|---|
| Chromium upgrades | Pin version; test every update | Not your concern |
| Security patches | Manual; CVEs ship in Chromium | Handled by provider |
| Scaling concurrency | Manual pool tuning or infra scaling | Auto-scaled by API |
| Monitoring | Browser health, memory, zombie processes | HTTP error rates only |
| Serverless | Lambda layers, size workarounds | Works out of the box |
| Docker image | 1 GB+ (Chromium included) | Lightweight (no Chromium) |
| On-call incidents | OOM crashes, process leaks | HTTP 5xx from provider |
For a deeper look at security considerations when self-hosting Chromium, see the PDF API security guide.
CSS and Rendering Compatibility
Because FUNBREW PDF uses a Chromium-based engine, the PDF output is visually identical to Puppeteer output for the vast majority of templates. The same CSS engine, same font rendering, same @page handling.
Two edge cases to check when evaluating:
page.emulateMediaType('screen'): If you called this in Puppeteer, note that the API defaults toprintmedia type. Audit your@media screenand@media printrules.page.evaluate()DOM manipulation: The API does not run application JavaScript. Move that logic into your template rendering step.
For detailed CSS tips during migration, see HTML to PDF CSS tips.
How to Decide
Use the decision tree below to choose the right approach for your situation.
Use Self-Hosted Puppeteer If
- You are prototyping and the volume is under 30 PDFs/month (cost is essentially zero)
- Your security policy prohibits sending HTML to external services
- You need
page.evaluate()to run complex JavaScript before generating the PDF (and cannot move that logic to server-side rendering) - You are already heavily invested in Puppeteer for browser testing, and PDF generation is a minor feature
Use a Managed PDF API If
- PDF generation is a non-trivial part of your product
- You are on a serverless infrastructure (Lambda, Vercel, Cloudflare Workers)
- Your Puppeteer setup is already causing OOM crashes, Chromium upgrade breakages, or CI/CD slowdowns
- You want to keep Docker images lean
- You need to scale concurrency beyond what a single server can handle
The Gradual Migration Path
You do not have to migrate all at once. A common pattern is to introduce a wrapper function:
// Thin wrapper — start by calling Puppeteer
async function generatePdf(html, options = {}) {
// Phase 1: Puppeteer
return generatePdfWithPuppeteer(html, options);
// Phase 2: flip to API when ready
// return generatePdfWithApi(html, options);
}
Migrate one PDF type at a time (invoices first, then reports, then certificates), using a feature flag to control the cutover. This gives you a rollback path while the team builds confidence in the API output. See the full migration procedure in the Puppeteer to PDF API migration guide.
Quick-Start Code
If you decide to try the API, here is the smallest possible Node.js example:
async function generatePdf(html) {
const response = await fetch('https://pdf.funbrew.cloud/api/v1/pdf/generate', {
method: 'POST',
headers: {
'X-API-Key': process.env.FUNBREW_PDF_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
html,
options: {
format: 'A4',
margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' },
print_background: true,
},
}),
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return Buffer.from(await response.arrayBuffer());
}
Test your HTML in the Playground before writing any code. The free plan gives you 30 PDFs/month at no cost. For Python, PHP, Ruby, and Go examples, see the API quickstart by language.
Summary
| Puppeteer (self-hosted) | Managed PDF API | |
|---|---|---|
| Cold start | 3–8 s (Lambda), 1–3 s (warm) | None |
| Memory per request | 200–500 MB | < 10 MB |
| Docker image | 1 GB+ | ~150 MB |
| Concurrency ceiling | Server RAM bound | Rate-limit bound |
| Annual maintenance | 20–50 engineer-hours | Near zero |
| Break-even | ~30 PDFs/month | Starts free |
Puppeteer is a reasonable starting point. A managed PDF API is usually the better choice once PDF generation becomes a real production workload — the operational savings outweigh the per-request cost at almost every realistic volume. Try the Playground to verify output quality against your templates before committing.
Related
- Puppeteer to PDF API Migration Guide — Step-by-step migration with before/after code
- HTML to PDF API Comparison 2026 — Broader comparison including wkhtmltopdf and Gotenberg
- PDF API Quickstart by Language — Node.js, Python, PHP, Ruby, Go examples
- Serverless PDF API Guide — Lambda and Cloudflare Workers deployment
- Docker and Kubernetes PDF API Guide — Container-based deployment patterns
- PDF API Error Handling Guide — Retry logic and resilience patterns
- PDF API Security Guide — Security best practices for PDF generation