PDFPipe

Serverless functions / Vercel

Playwright on Vercel: an install step the build never runs

Deploy succeeds, first request fails with "Executable doesn't exist at ...ms-playwright/chromium-...".

What is actually wrong

Playwright's browsers are installed by a separate command, not by the package install. A deployment build runs your install and build scripts; it does not run playwright install, and even if it did, the browsers land in a home cache directory that is not part of the function output. This is a different failure from the Puppeteer one: there the browser was too big, here it was never fetched.

What Vercel gives you to work with

serverless functions built from your repository, plus an edge runtime that is a different environment entirely.

  • Packaging: there is a cap on the deployed size of a function, and a full browser install is comfortably past it. The bundler also traces which files a function needs, so a browser that is fetched rather than imported is invisible to it and gets left out of the bundle.
  • Filesystem: read-only except /tmp. On the edge runtime there is no filesystem at all, and no child_process either, so nothing that spawns a binary can run there under any configuration.
  • Processes: a Node function can spawn a process for the duration of the request. There is a maximum execution duration and it is short by the standards of a cold browser launch.

The fix, if you are keeping Playwright

There is no comfortable answer. Adding the install to the build command puts the browsers on the build machine, and the function output does not include them. Redirecting the cache into the project directory makes them part of the output and then the output is too large. The honest options are a container-based deployment target, or moving the render off the request path entirely.

What still hurts after that

Every workaround here trades the size limit against the install step and one of the two always wins. Teams usually discover this after the endpoint is written, because everything works locally where both the install and the cache are exactly where Playwright expects them.

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 Vercel 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 Vercel at all?

There is no comfortable answer. Adding the install to the build command puts the browsers on the build machine, and the function output does not include them. Redirecting the cache into the project directory makes them part of the output and then the output is too large. The honest options are a container-based deployment target, or moving the render off the request path entirely. 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 Vercel 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.

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.