Blog

gPdf vs Puppeteer: 800 MB Chromium ne zaman yanlış cevaptır

Puppeteer her web page'i PDF yapabilir, ama çoğu document işinde kullanmadığınız headless browser için ödeme yaparsınız. 2026 için pratik karşılaştırma.

Bugün “Puppeteer PDF alternative” diye aradıysanız, asıl soru muhtemelen şudur:

“Tek bir invoice yazdırmak için serverless function neden 2 seconds cold start alıyor ve 900 MB RAM kullanıyor?”

Puppeteer harika bir tool. Ama birçok ekibin yaptığı iş, yani structured data’yı predictable PDF’e çevirmek için fazla büyük. Bu yazı, Puppeteer’ı production’a taşımak üzereyken daha mantıklı bir seçenek olup olmadığını düşünen ekipler için.

Puppeteer ile aslında ne ship ediyorsunuz

npm install puppeteer çalıştığında, transitive deps hariç yaklaşık 170 MB Chromium build indirirsiniz. Runtime’da headless Chromium tek page render için 600-900 MB resident memory ve browser’ı kaldırmak için 1-2 seconds cold start ister. Her render:

  1. Browser process’i başlatır ya da pool reuse eder.
  2. Yeni tab açar.
  3. HTML / URL’ye navigate eder.
  4. domcontentloaded ve çoğu zaman fonts, images, web components bekler.
  5. page.pdf() çalıştırıp painted page’i Chromium PDF engine üzerinden serialize eder.
  6. Tab’i kapatır.

Bu whole-web-platform tax’tir. 90-page legal contract için de, beş satırlık one-page shipping label için de aynı tax’i ödersiniz.

Input gerçekten HTML ise ve CSS layout, JavaScript, web fonts gerekiyorsa bu adil olabilir. Invoices, labels, receipts, tickets, statements ve certificates için çoğu zaman israftır.

Puppeteer nerede kazanır

Önce dürüst olun:

  • Faithful HTML/CSS rendering. Design system HTML emit ediyor ve pixel-identical PDF gerekiyorsa Puppeteer en iyi seçenektir. Bu Chrome printing’dir.
  • Web-platform features. SVG filters, CSS Grid edge cases, web components, JavaScript content, third-party iframes çalışır.
  • Visual debugging. Render ortasında screenshot alabilir, headless mode’a DevTools açabilirsiniz.
  • Zero translation step. Content zaten webpage ise schema mapping yoktur. page.goto(url); await page.pdf() tüm pipeline’dır.

Bu maddelerden ikisi gerçek iş yükünüzü tarif ediyorsa, geçmeyin. Puppeteer doğru cevaptır.

Puppeteer nerede kaybeder

Diğer her yerde cost hızla birikir.

Serverless memory ve cold start

Puppeteer çalışan tipik Node 20 Lambda veya Cloudflare Container:

Metric Typical value
Container image size 250-400 MB (Chromium + Node + your code)
Cold-start time 1.8 - 2.5 seconds
Warm RAM per render 600 - 900 MB
Concurrent renders per 1 GB instance 1 (sometimes 2 if pages are tiny)

Invoice service ayda 100.000 render yapıyorsa, hiçbir render JavaScript execution istemese bile her cold container için browser boot bedeli ödersiniz.

Container font tuzağı

Chromium default font set ile gelir; CJK, Cyrillic, Devanagari, Arabic ve pek çok glyph genelde eksiktir. Production’da şöyle görünür:

Tokyo office Q3 2025 invoice ▢▢▢▢ 2025年第3四半期 basar. Customer escalates. Ekip bir sprint’i Dockerfile fonts ve CSS fallback debug’a harcar.

Sadece NotoSans CJK image’a ~50 MB ekler. Global Noto fallback set ~250 MB ekleyebilir. Tek bir Japanese invoice için Chromium ve dev bir font paketi taşırsınız.

Determinism

Puppeteer renders Chromium versions arasında byte-identical değildir. Patch upgrade kerning, font baselines veya page breaks değiştirebilir. PDF diff tests varsa her Chromium update küçük bir investigation olur.

Render-time JavaScript

“Static” HTML bile parse, layout, paint ve serialize edilir. Warm process’te bu 80-400 ms per page olabilir. Aynı one-page invoice JSON olarak binary renderer’a giderse 3-8 ms sürer.

gPdf nereye oturur

gPdf modeli tersine çevirir: belgeyi tarayıcının çizeceği HTML olarak değil, yapılandırılmış JSON (DocumentRequest) olarak tanımlarsınız. Rust renderer WebAssembly’ye derlenir ve PDF’i doğrudan üretir. Browser yok, DOM yok, JavaScript layout pass yok.

Gerçek HTML-shaped problems için sınırlayıcıdır. Ama invoice / label / receipt / statement / certificate sınıfında JSON-first model daha uygundur:

  • Veri zaten yapılandırılmıştır. Invoice zaten genelde { customer, lines, totals, taxes, notes } objesidir. Onu HTML’e çevirip browser’a layout olarak tekrar okutmak istemezsiniz.
  • Layout contract olur. font_size: 11 her zaman 11 point, gap: 8 her zaman 8 point demektir.
  • Çıktı byte-identical’dır. Same input → same bytes. PDF diff anlamlıdır.
  • Cold start runtime startup’tır, browser boot değil. Cloudflare Workers’ta V8 isolate 5-20 ms içinde başlar; WASM module aynı isolate’ta hot kalır.

gPdf ile typical one-page invoice render edge’de 3-5 ms p50 wall-clock verir. Puppeteer warm path’ten yaklaşık iki order, cold path’ten üç order hızlıdır.

Decision matrix

Workload Use Puppeteer Use gPdf
Existing HTML report → PDF ✅ first choice ⚠️ requires rewrite
Invoices, statements, receipts ⚠️ heavy hammer ✅ first choice
Shipping labels with barcodes ❌ avoid (font issues) ✅ first choice
E-invoice (Factur-X / ZUGFeRD / EN 16931) ❌ no built-in support ✅ built-in
PDF/A long-term archival ⚠️ needs Ghostscript pass ✅ built-in profiles
Pixel-faithful design system mockups ✅ first choice ❌ wrong tool
Charts that need real D3 / Recharts ✅ first choice ❌ wrong tool
Tickets, certificates, name-tags ⚠️ overkill ✅ first choice
Anything that needs JavaScript at render time ✅ only choice ❌ wrong tool

Sağ kolon üçten fazla satırda kazanıyorsa savings küçük değildir.

Gerçek karşılaştırma: one-page invoice

Aynı content, paper size, NotoSans fonts ve PDF/A-3b profile:

Puppeteer (warm Lambda, 1 GB) gPdf (warm Cloudflare Worker)
p50 latency 180 ms 3.4 ms
p99 latency 420 ms 8 ms
Cold-start penalty +1800 ms first render +12 ms first render
Memory at peak 720 MB 18 MB
Image / module size 280 MB 4.5 MB
CJK glyphs ❌ unless explicit install ✅ embedded NotoSans CJK
Maliyet / 100.000 render ~$240 (Lambda compute) ~$5 (gPdf Basic plan)

Son satır şaşırtır, ama teaser price değildir. Yapısaldır. Chromium boot, browser memory veya container cold starts amortize etmiyoruz.

“Ama $5/100.000 sayfa çok ucuz geliyor. Catch ne?”

Catch şu: browser çalıştırmıyoruz. Warm V8 isolate üstünde binary renderer’ın cost’u milliseconds CPU ve kilobytes memory’dir. Puppeteer fiyatı almak çalıştırmadığımız infrastructure için charge etmek olurdu.

Ne zaman yine Puppeteer seçilmeli

Dürüst cevap her zaman “gPdf kullan” değildir:

  1. Puppeteer production’da çalışıyor ve sorun çıkarmıyor. Sırf değiştirmek için migrate etmeyin. Compute bill 400 USD/ay üstüne çıktığında veya cold-start SLA bozduğunda gPdf’i değerlendirin.
  2. Documents gerçekten existing webpages. Charts ve dynamic content içeren user-generated 60-page report JSON migration değil redesign’dır.
  3. Web preview ile pixel-perfect parity gerekiyor. Bazı workflows iki tarafta da Chromium renderer ister.

Hiçbiri geçerli değilse matematik nettir: smaller deploy, lower latency, lower bill, byte-identical output ve daha az font-install drama.

Gerçek iş yükü nasıl taşınır

Genelde document type başına 1-2 day spike:

  1. Bir document seçin; en karmaşık olanı değil, en yüksek volume olanı.
  2. HTML template’in logical sections’ını gPdf JSON elements’e (text, box, table, barcode, image) map edin.
  3. Playground içinde gerçek DocumentRequest ile iterate edin.
  4. Mevcut data-shape’ten JSON emit eden küçük mapper yazın.
  5. Bir hafta Puppeteer endpoint’e karşı A/B yapın. PDFs diff edin. Karar verin.

Çoğu ekip JSON model’i bir günde kavrar. Zor olan yeni tool değil, eski template’teki HTML/CSS gymnastics’i çözmektir.

TL;DR

Puppeteer web pages için doğru cevaptır. Documents için ise document’ı data olarak tanımlama şeklindeki küçük one-time cost’tan kaçmak için her render’da 100-200× tax ödersiniz. Fleet invoices, labels, receipts, statements veya “same shape, different values” documents render ediyorsa gPdf gibi edge-native renderer daha hızlı, küçük, ucuz ve deterministic olur.

Playground deneyin. Gerçek edge worker, signup yok, browser response under 5 ms.