Reference
Why your PDF renderer will not deploy
The generic advice is that a browser is large and that this causes problems. That is true and it does not help anyone at four in the afternoon with a failing deploy. What helps is the specific limit: 250 megabytes unzipped, 64 megabytes of shared memory, a 500 megabyte slug, a plan with no package manager. Each page below has one of those, the error text it produces, the fix, and what the fix still costs afterwards.
Renderers covered: Puppeteer, Playwright, wkhtmltopdf, WeasyPrint. Once the deployment works, the next set of problems is in the output rather than the infrastructure: what goes wrong when HTML becomes a PDF.
Serverless functions
AWS Lambda
a function that is handed a request, runs, and is frozen again. The execution environment is reused between invocations but you do not control when it is torn down.
- Puppeteer: the 250 MB package limit
- Playwright: browsers that are not in your bundle
- wkhtmltopdf: a binary with no libraries to link against
- WeasyPrint: the libraries pip cannot install
Vercel
serverless functions built from your repository, plus an edge runtime that is a different environment entirely.
- Puppeteer: a bundler that cannot see the browser
- Playwright: an install step the build never runs
Netlify Functions
AWS Lambda underneath, with Netlify's own bundling step in front of it.
- Puppeteer: esbuild does not copy binaries
Azure Functions
a function host that runs your code inside an environment it manages, with the amount of control depending entirely on which hosting plan you chose.
- Puppeteer: a plan that installs nothing
- WeasyPrint: GLib is not a pip package
Managed containers
Google Cloud Run
your container image, started on demand and scaled to zero, with requests routed into it.
- Puppeteer: 64 MB of shared memory
- Playwright: a two gigabyte image on a cold start
- WeasyPrint: a small image that renders differently
Docker
an image you build, which means every system library and font is present only because you put it there.
- Puppeteer: an image with no libraries and no fonts
- Playwright: browsers downloaded outside the image layer
- wkhtmltopdf: the apt package is the wrong build
- WeasyPrint: a Pango version that does not match
Fly.io
your image running in a lightweight virtual machine, one per instance, started and stopped on demand.
- Puppeteer: a machine too small to hold a browser
- Playwright: image size is boot time
Container orchestration
Kubernetes
the same container as Docker, scheduled onto a node with a memory limit the kernel enforces by killing things.
- Puppeteer: OOMKilled with no stack trace
- Playwright: an image that slows every scale-up
- wkhtmltopdf: processes that never exit
Platform as a service
Heroku
a slug built by buildpacks from your repository, run on a dyno with an ephemeral filesystem.
- Puppeteer: the 500 MB slug and the R14 that follows
- wkhtmltopdf: a binary with nowhere to live
- WeasyPrint: an Aptfile and a library path
Render
either a native runtime built from your repository, or a container built from your Dockerfile.
- Puppeteer: a native runtime with no package manager
- Playwright: an install step with nowhere to write
Railway
a build from your repository, either through an automatic builder or your own Dockerfile.
- Puppeteer: a builder that installs language packages only
- wkhtmltopdf: an apt package the build never runs
CI runners
GitHub Actions
an ephemeral runner with a lot of memory, a browser already installed, and a job that ends when the workflow does.
- Puppeteer: an environment nothing else resembles
- Playwright: a browser download on every run