Container orchestration / Kubernetes
Playwright on Kubernetes: an image that slows every scale-up
Autoscaling responds to load and the new pods take a long time to become ready, so the spike is over before the capacity arrives.
What is actually wrong
The image carries browsers, and a pod scheduled onto a node that has not pulled it waits for the pull before the container starts. The larger the image, the longer the gap between deciding to scale and having capacity, and that gap is dead time exactly when it matters.
What Kubernetes gives you to work with
the same container as Docker, scheduled onto a node with a memory limit the kernel enforces by killing things.
- Packaging: no image size limit, but a large image slows every scale-up, because a new pod on a cold node waits for the pull.
- Filesystem: as configured. /dev/shm defaults to 64 MB and is the single most common cause of browser crashes in a cluster.
- Processes: full control inside the pod. Memory limits are enforced by the kernel out-of-memory killer, which terminates the process without a stack trace and without an application-level error.
The fix, if you are keeping Playwright
Build a single-browser image on a slim base rather than starting from the full one, and pre-pull it onto nodes so the pull is not on the critical path.
# A DaemonSet that pulls the image on every node ahead of time, so
# scale-up does not pay for the pull.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: renderer-image-prepull
spec:
template:
spec:
containers:
- name: prepull
image: your-registry/renderer:1.4.2
command: ["sh", "-c", "sleep infinity"]
resources:
requests: { cpu: "1m", memory: "8Mi" }What still hurts after that
Pre-pulling reserves a slot on every node for an image most of them are not running, and it has to be updated in step with the deployment or it warms the wrong version. The shared memory and out-of-memory problems from the Puppeteer case apply here unchanged, because underneath the different library it is the same browser.
What Playwright is genuinely good at
a better API than Puppeteer for waiting on real page state, and a first-party dependency installer that knows which system packages the browser needs. That is worth keeping in view: the problem on this page is where it runs, not what it produces. Playwright is a browser automation library that installs its browsers outside node_modules, into a shared cache directory, at install time. It needs the same browser and shared libraries as Puppeteer, plus an install step that runs separately from the package install and writes to a location your deployment probably does not copy.
The other shape of the answer
Every fix above keeps the renderer inside your deployment, which means the constraint stays yours: the package size, the shared libraries, the fonts, the memory, the patching. The alternative is an HTTP call from Kubernetes to something that already has a browser in it, which turns all of the above into a request and a response. That is what this API is, and it is the reason these pages exist. Whether that trade is right depends on whether rendering is a thing you want to operate.
Frequently asked
Can Playwright run on Kubernetes at all?
Build a single-browser image on a slim base rather than starting from the full one, and pre-pull it onto nodes so the pull is not on the critical path. Whether that is worth doing is a separate question from whether it is possible, and the section above on what still hurts is the honest answer to it.
Why does it work locally?
Because a development machine has root, a package manager, several gigabytes of memory, a full font set and no packaging limit. Every one of those is a thing Kubernetes constrains. A renderer that works locally and fails on deploy has not regressed: it has met the first environment that does not give it everything.
Related failures
The same platform with a different renderer, and the same renderer on other platforms.
Puppeteer on Kubernetes: OOMKilled with no stack trace
The pod restarts. There is no application error, no exception, and the last log line is whatever was printed before the render started. The pod status says OOMKilled.
wkhtmltopdf on Kubernetes: processes that never exit
Over hours or days the pod slows down and eventually stops responding. Process listings show a growing number of wkhtmltopdf processes in a defunct state.
Playwright on AWS Lambda: browsers that are not in your bundle
At runtime, not at deploy time: "Executable doesn't exist at /home/sbx_user1051/.cache/ms-playwright/chromium-.../chrome-linux/chrome".
Playwright on Vercel: an install step the build never runs
Deploy succeeds, first request fails with "Executable doesn't exist at ...ms-playwright/chromium-...".
Playwright on Google Cloud Run: a two gigabyte image on a cold start
Everything works, and the first request after a quiet period takes many seconds before any of your code runs.
Playwright on Docker: browsers downloaded outside the image layer
The image builds, and at runtime the browser is missing, or the container downloads it again on every start.
Every renderer, on every platform
The full grid, grouped by what kind of environment the platform is.
What goes wrong when HTML becomes a PDF
Once it deploys, the next set of problems is in the output rather than the infrastructure.
If the render moves off this platform, none of the above is your problem to keep solving. 100 free documents a month, and a playground that needs no signup.