Blog

gPdf vs Puppeteer: saat 800 MB Chromium adalah jawaban yang salah

Puppeteer bisa membuat PDF dari halaman web apa pun, tetapi sering kali Anda membayar headless browser yang tidak dipakai. Perbandingan praktis untuk 2026.

Jika hari ini Anda mencari “Puppeteer PDF alternative”, pertanyaan sebenarnya mungkin:

“Kenapa serverless function saya cold-start 2 seconds dan memakai 900 MB RAM hanya untuk mencetak satu invoice?”

Puppeteer adalah tool yang bagus. Namun untuk banyak tim, ia terlalu besar untuk pekerjaan yang dilakukan: mengubah structured data menjadi PDF yang predictable. Ini untuk tim yang hampir membawa Puppeteer ke production dan bertanya apakah ada pilihan yang lebih masuk akal.

Apa yang sebenarnya Anda ship dengan Puppeteer

Saat npm install puppeteer, Anda mengambil sekitar 170 MB Chromium build sebelum transitive deps. Saat runtime, headless Chromium butuh 600-900 MB resident memory untuk satu page render dan 1-2 seconds cold start untuk menjalankan browser. Setiap render harus:

  1. Boot browser process atau reuse pool.
  2. Membuka tab baru.
  3. Navigate ke HTML / URL.
  4. Menunggu domcontentloaded, biasanya juga fonts, images, web components.
  5. Menjalankan page.pdf() yang serialize painted page lewat Chromium PDF engine.
  6. Menutup tab.

Ini pajak seluruh web platform. Anda membayarnya untuk legal contract 90 halaman maupun shipping label satu halaman dengan lima baris.

Untuk HTML-to-PDF yang sungguh membutuhkan CSS layout, JavaScript, web fonts, pajak itu wajar. Untuk invoices, labels, receipts, tickets, statements, dan certificates, sering kali itu berlebihan.

Di mana Puppeteer menang

Jujurlah sebelum migrasi:

  • HTML/CSS fidelity. Jika design system Anda emit HTML dan PDF harus pixel-identical, Puppeteer sulit dikalahkan. Ini Chrome printing.
  • Web-platform features. SVG filters, CSS Grid edge cases, web components, JavaScript content, third-party iframes berjalan.
  • Visual debugging. Anda bisa screenshot saat render dan membuka DevTools untuk headless mode.
  • Zero translation step. Jika content sudah webpage, tidak ada schema mapping. page.goto(url); await page.pdf() adalah pipeline.

Jika dua poin itu menggambarkan workload nyata, jangan pindah. Puppeteer adalah jawaban yang benar.

Di mana Puppeteer kalah

Untuk sisanya, cost cepat menumpuk.

Memory dan cold start di serverless

Node 20 Lambda atau Cloudflare Container umum dengan Puppeteer:

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)

Jika invoice service menangani 100.000 render/bulan, Anda membayar browser boot di setiap cold container, padahal tidak ada render yang butuh JavaScript execution.

Jebakan font di containers

Chromium datang dengan default font set yang sering tidak cukup untuk CJK, Cyrillic, Devanagari, Arabic, dan banyak glyph. Di production terlihat seperti:

Invoice Q3 2025 kantor Tokyo mencetak ▢▢▢▢ 2025年第3四半期. Customer escalates. Tim menghabiskan sprint untuk Dockerfile fonts dan CSS fallback.

NotoSans CJK saja menambah ~50 MB ke image. Global Noto fallback set bisa menambah ~250 MB. Anda membayar Chromium dan paket font raksasa untuk satu invoice Jepang.

Determinism

Puppeteer renders tidak byte-identical antar versi Chromium. Patch upgrade bisa menggeser kerning, font baselines, atau page breaks. Jika Anda punya PDF diff tests, setiap update Chromium menjadi investigasi.

Render-time JavaScript

HTML “static” tetap harus parse, layout, paint, dan serialize. Secara empiris 80-400 ms per page pada warm process. One-page invoice yang sama lewat JSON ke binary renderer bisa 3-8 ms.

Di mana gPdf cocok

gPdf membalik model: dokumen tidak dideskripsikan sebagai HTML untuk dilukis browser. Dokumen dideskripsikan sebagai structured JSON (DocumentRequest), lalu renderer Rust yang dikompilasi ke WebAssembly emit PDF langsung. Tidak ada browser, DOM, atau JavaScript layout pass.

Ini membatasi masalah yang benar-benar HTML-shaped. Tetapi untuk invoice / label / receipt / statement / certificate, model JSON-first sering lebih pas:

  • Data Anda sudah structured. Invoice biasanya sudah berupa { customer, lines, totals, taxes, notes }. Tidak perlu dirender ke HTML lalu dibaca kembali oleh browser.
  • Layout menjadi contract. font_size: 11 selalu 11 points; gap: 8 selalu 8 points.
  • Output byte-identical. Same input → same bytes. PDF diff menjadi berguna.
  • Cold start adalah runtime startup, bukan browser boot. V8 isolate di Cloudflare Workers mulai dalam 5-20 ms; WASM module tetap hot di isolate yang sama.

Render invoice satu halaman dengan gPdf umumnya 3-5 ms p50 wall-clock di edge. Sekitar dua order lebih cepat dari Puppeteer warm path dan tiga order dari cold path.

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

Jika kolom kanan menang di lebih dari tiga baris, penghematannya tidak kecil.

Perbandingan nyata: one-page invoice

Konten, paper size, NotoSans fonts, dan PDF/A-3b profile yang sama:

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
Biaya / 100.000 render ~$240 (Lambda compute) ~$5 (gPdf Basic plan)

Baris terakhir sering mengejutkan. Itu bukan harga umpan, melainkan struktural. Kami tidak perlu amortize Chromium boot, browser memory, atau container cold starts.

“Tapi $5/100.000 halaman terlalu murah. Apa catch-nya?”

Catch-nya adalah kami tidak menjalankan browser. Binary renderer pada warm V8 isolate hanya butuh milliseconds CPU dan kilobytes memory. Harga Puppeteer berarti menagih infrastruktur yang tidak kami jalankan.

Kapan tetap memilih Puppeteer

Jawaban jujur tidak selalu “pakai gPdf”:

  1. Puppeteer sudah production dan baik-baik saja. Jangan migrasi untuk gaya. Evaluasi gPdf saat Puppeteer mulai sakit: compute bill di atas $400/bulan atau cold start merusak SLA.
  2. Dokumen Anda memang existing webpages. Report 60 halaman user-generated dengan charts dan dynamic content bukan JSON migration; itu redesign.
  3. Butuh pixel-perfect parity dengan web preview. Beberapa workflow membutuhkan Chromium sebagai renderer di kedua sisi.

Jika tidak berlaku, hitungannya jelas: deploy lebih kecil, latency lebih rendah, biaya lebih rendah, output byte-identical, dan drama font lebih sedikit.

Cara migrasi workload nyata

Biasanya 1-2 hari spike per document type:

  1. Pilih satu document, mulai dari volume tertinggi, bukan yang paling kompleks.
  2. Map logical sections HTML template ke gPdf JSON elements (text, box, table, barcode, image).
  3. Iterasi di Playground dengan DocumentRequest nyata.
  4. Buat mapper kecil dari data-shape saat ini ke JSON.
  5. Jalankan A/B dengan endpoint Puppeteer selama seminggu. Diff PDFs. Putuskan.

Kebanyakan tim memahami model JSON dalam sehari. Bagian sulit bukan tool baru, tetapi membongkar HTML/CSS gymnastics dari template lama.

TL;DR

Puppeteer tepat untuk web pages. Untuk documents, Anda membayar tax 100-200× di setiap render agar tidak perlu sekali mendeskripsikan dokumen sebagai data. Jika fleet Anda membuat invoices, labels, receipts, statements, tickets, atau bentuk yang sama dengan nilai berbeda, renderer edge-native seperti gPdf akan lebih cepat, kecil, murah, dan deterministic.

Coba di Playground. Ini edge worker nyata, tanpa signup, dengan response browser di bawah 5 ms.