Si hoy buscó “Puppeteer PDF alternative” y llegó aquí, la pregunta real probablemente es una variante de:
“¿Por qué mi función serverless tarda 2 segundos en arrancar en frío y usa 900 MB de RAM solo para imprimir una factura?”
Puppeteer es una herramienta brillante. También está sobredimensionada para el trabajo para el que muchos equipos la usan: convertir datos estructurados en un PDF predecible. Este artículo es para el equipo que está a punto de llevar Puppeteer a producción y sospecha en silencio que debe existir una opción más razonable.
Cubrirá dónde Puppeteer justifica su peso, dónde no, y cómo se ve la matriz real de trade-offs en 2026.
Qué está desplegando realmente con Puppeteer
Cuando ejecuta npm install puppeteer, descarga una build de Chromium de unos 170 MB antes de contar dependencias transitivas. En tiempo de ejecución, Chromium sin interfaz gráfica suele necesitar 600-900 MB de memoria residente para renderizar una sola página, y 1-2 segundos de arranque en frío para levantar el navegador. Cada renderizado tiene que:
- Arrancar el proceso del navegador, o reutilizar un pool.
- Abrir una pestaña nueva.
- Navegar a su HTML o URL.
- Esperar
domcontentloaded, y normalmente también fuentes, imágenes y web components. - Ejecutar
page.pdf(), que serializa la página pintada a través del motor PDF de Chromium. - Cerrar la pestaña.
Ese es el impuesto de toda la plataforma web. Lo paga tanto si su documento es un contrato legal de 90 páginas con gráficos SVG incrustados como si es una etiqueta de envío de una página con cinco líneas de texto.
Para casos HTML a PDF donde la entrada realmente necesita maquetación CSS, contenido controlado por JavaScript, web fonts y el resto de la plataforma web, ese impuesto es razonable. Para todo lo demás — facturas, etiquetas, recibos, entradas, estados de cuenta y certificados — es quemar dinero.
Dónde gana Puppeteer
Sea honesto con esto primero; si no, su equipo volverá a cuestionar la decisión más tarde:
- Renderizado HTML/CSS fiel. Si su sistema de diseño emite HTML y quiere PDF con fidelidad píxel a píxel de ese HTML, Puppeteer es imbatible. Es literalmente Chrome imprimiendo.
- Funciones de la plataforma web. SVG con filtros, casos límite de CSS Grid, web components, contenido evaluado por JavaScript e iframes de terceros: todo funciona.
- Depuración visual. Puede tomar una captura a mitad del renderizado, abrir DevTools contra el modo sin interfaz gráfica y ver exactamente lo que ve el renderizador.
- Sin paso de traducción. Si su contenido ya es una página web, no hay mapeo de esquema.
page.goto(url); await page.pdf()es toda la canalización.
Si dos de esos puntos describen su carga real, no cambie. Puppeteer es la respuesta correcta.
Dónde Puppeteer pierde, y mucho
En todo lo demás, la pila de costes se acumula rápido.
Memoria y arranque en frío en serverless
Una Lambda Node 20 típica o un Cloudflare Container con Puppeteer:
| Métrica | Valor típico |
|---|---|
| Tamaño de imagen de contenedor | 250-400 MB (Chromium + Node + su código) |
| Tiempo de arranque en frío | 1,8-2,5 segundos |
| RAM caliente por renderizado | 600-900 MB |
| Renderizados concurrentes por instancia de 1 GB | 1 (a veces 2 si las páginas son muy pequeñas) |
Si su servicio de facturas procesa 100.000 renderizados al mes, paga la energía de arrancar navegador en cada contenedor frío, aunque ninguno de esos renderizados necesite ejecutar JavaScript.
La trampa de las fuentes en contenedores
Chromium viene con un conjunto de fuentes por defecto que normalmente no incluye CJK, cirílico, devanagari, árabe ni una larga cola de glifos específicos de cada escritura. Descubrirlo en producción se ve así:
La factura del Q3 2025 para la oficina de Tokio imprime
▢▢▢▢ 2025年第3四半期. El cliente escala el problema. Su equipo dedica un sprint a depurar instalaciones de fuentes en Dockerfile y fallback CSS.
Incrustar solo NotoSans CJK añade ~50 MB a la imagen. Incrustar un conjunto global de fallback Noto añade ~250 MB. Está pagando Chromium y una catedral de fuentes solo para imprimir una factura japonesa.
Determinismo
Los renderizados de Puppeteer no son idénticos byte a byte entre versiones de Chromium. Una actualización de parche puede desplazar sutilmente kerning, líneas base de fuentes o posiciones de salto de página. Si tiene una suite que compara PDF — y debería tenerla — cada actualización de Chromium se convierte en una pequeña excavación arqueológica: qué cambió y si era intencional.
JavaScript en tiempo de renderizado
Incluso una página HTML “estática” debe analizarse, maquetarse, pintarse y serializarse. Empíricamente, eso son 80-400 ms por página en un proceso caliente. Casi todo es maquetación, no pintura.
Como comparación: un servidor que devuelve JSON directamente a un renderizador binario tarda 3-8 ms para la misma factura de una página. Volvemos a esos números abajo.
Dónde encaja gPdf
gPdf invierte el modelo: en lugar de describir el documento como HTML y pedir a un navegador que lo pinte, lo describe como JSON estructurado (DocumentRequest) y un renderizador Rust compilado a WebAssembly emite el PDF directamente. No hay navegador. No hay DOM. No hay pasada de maquetación JavaScript.
Suena restrictivo, y lo es para problemas con forma de HTML. Pero para la clase de documentos de factura / etiqueta / recibo / estado de cuenta / certificado, el modelo JSON-first encaja mejor:
- Los datos ya son estructurados. Su factura ya existe en alguna parte como un objeto
{ customer, lines, totals, taxes, notes }. No quiere convertir eso primero a HTML y pedir al navegador que vuelva a leer el HTML como maquetación. Quiere ir directo de datos a PDF. - La maquetación se convierte en contrato. Cuando
font_size: 11siempre significa 11 puntos ygap: 8siempre significa 8 puntos, dos ingenieros revisando una PR ven exactamente la misma salida. No hay hueco de interpretación dedisplay: flex. - La salida es idéntica byte a byte. Misma entrada -> mismos bytes. Puede hacer
git diffde dos PDF y ver solo lo que cambió. - El arranque en frío es el inicio del entorno de ejecución, no el arranque del navegador. Un isolate V8 en Cloudflare Workers inicializa en 5-20 ms. El módulo WASM queda caliente en memoria entre invocaciones del mismo isolate.
Un renderizado típico de gPdf para una factura de una página mide 3-5 ms p50 de tiempo total en el edge, servido desde la colo de Cloudflare que recibió al usuario. Eso es unas dos órdenes de magnitud más rápido que la ruta caliente de Puppeteer y tres órdenes de magnitud más rápido que su ruta fría.
La matriz de decisión
Use la tabla que realmente llevaría a una revisión de diseño técnico.
| Carga de trabajo | Use Puppeteer | Use gPdf |
|---|---|---|
| Informe HTML existente -> PDF | ✅ primera opción | ⚠️ requiere reescritura |
| Facturas, estados de cuenta, recibos | ⚠️ martillo pesado | ✅ primera opción |
| Etiquetas de envío con códigos de barras | ❌ evítelo (problemas de fuentes) | ✅ primera opción |
| Factura electrónica (Factur-X / ZUGFeRD / EN 16931) | ❌ sin soporte integrado | ✅ integrado |
| Archivo PDF/A a largo plazo | ⚠️ requiere pasada Ghostscript | ✅ perfiles integrados |
| Mockups con fidelidad píxel a píxel de un sistema de diseño | ✅ primera opción | ❌ herramienta equivocada |
| Gráficos que necesitan D3 / Recharts reales | ✅ primera opción | ❌ herramienta equivocada |
| Entradas, certificados, name-tags | ⚠️ sobredimensionado | ✅ primera opción |
| Cualquier cosa que necesita JavaScript al renderizar | ✅ única opción | ❌ herramienta equivocada |
Si está en la columna derecha en más de tres filas, el ahorro no es sutil.
Comparación real: renderizado de una factura de una página
Mismo contenido. Mismo tamaño de papel. Mismas fuentes (NotoSans). Mismo perfil de salida PDF/A-3b.
| Puppeteer (Lambda caliente, 1 GB) | gPdf (Cloudflare Worker caliente) | |
|---|---|---|
| Latencia p50 | 180 ms | 3,4 ms |
| Latencia p99 | 420 ms | 8 ms |
| Penalización de arranque en frío | +1800 ms primer renderizado | +12 ms primer renderizado |
| Memoria en pico | 720 MB | 18 MB |
| Tamaño de imagen / módulo | 280 MB | 4,5 MB |
| Glifos CJK | ❌ salvo instalación explícita | ✅ NotoSans CJK incrustado |
| Coste / 100.000 renderizados | ~240 USD (cómputo Lambda) | ~5 USD (plan gPdf Basic) |
Esa última fila suele sorprender. La brecha de coste es real y no es una oferta gancho: es estructural. No tenemos que amortizar arranque de Chromium, memoria de navegador ni arranques en frío de contenedores, así que el coste unitario por renderizado es realmente pequeño.
“Pero 5 USD/100.000 páginas suena demasiado barato. ¿Cuál es la trampa?”
La trampa es exactamente que no enviamos un navegador. El coste de ejecutar un renderizador binario en un isolate V8 caliente son milisegundos de CPU y kilobytes de memoria. Cobrar precios con forma de Puppeteer sería cobrar por infraestructura que no ejecutamos.
Cuándo debería seguir eligiendo Puppeteer
Seríamos la peor persona a la que preguntar si nuestra respuesta siempre fuera “use gPdf”. No lo es. Casos honestos:
-
Ya usa Puppeteer en producción y funciona. No migre por deporte. El momento correcto para evaluar gPdf es cuando Puppeteer empieza a doler: normalmente cuando la factura mensual de cómputo supera los 400 USD o cuando los arranques en frío rompen algún SLA aguas abajo.
-
Sus documentos son páginas web existentes, punto. Un informe de 60 páginas generado por usuarios, estilizado por su sistema de diseño, con gráficos anidados y contenido dinámico, no es una migración JSON. Es un rediseño.
-
Necesita paridad píxel a píxel con una vista previa web. Algunos flujos, por ejemplo “lo que se ve en el editor es lo que se imprime”, necesitan que Chromium sea el renderizador en ambos lugares.
Si ninguno de esos casos aplica, la matemática es directa: despliegue más pequeño, menor latencia, menor factura, salida idéntica byte a byte y cero drama de instalación de fuentes.
Cómo migrar una carga real
Si está lo bastante convencido para probar, la migración suele ser un spike de 1-2 días por tipo de documento, no una rearquitectura:
- Elija un documento. Empiece por el de mayor volumen, no por el más complejo.
- Mapee las secciones lógicas de su plantilla HTML a elementos JSON de gPdf (
text,box,table,barcode,image). - Use el Playground para iterar sobre un DocumentRequest real hasta que la salida coincida.
- Conecte la forma actual de sus datos a una pequeña función mapeadora que emita JSON.
- Haga A/B del nuevo endpoint contra el de Puppeteer durante una semana. Compare los PDF. Decida.
La mayoría de equipos entiende el modelo JSON en un día. La parte difícil no es la nueva herramienta: es desenredar la gimnasia HTML/CSS que la plantilla anterior acumuló con el tiempo.
TL;DR
Puppeteer es la respuesta correcta para páginas web. Para documentos, está pagando un impuesto de 100-200× en cada renderizado para evitar el pequeño coste inicial de describir el documento como datos. Si su flota renderiza facturas, etiquetas, recibos, estados de cuenta, entradas o cualquier otro documento que tiene “la misma forma cada vez, con valores distintos”, un renderizador edge-native como gPdf será mediblemente más rápido, más pequeño, más barato y más determinista.
Pruébelo en el Playground: es un worker real en el edge, sin registro, con respuesta en su navegador en menos de 5 ms.