PDFPipe

Platform as a service / Railway

Puppeteer on Railway: a builder that installs language packages only

The build succeeds, the deploy succeeds, and the browser fails to start with a missing shared library.

What is actually wrong

The automatic builder reads your manifest, infers a runtime and installs your language dependencies. It has no way to know that one of those dependencies is a browser needing twenty system libraries, because nothing in package.json says so. The npm install genuinely succeeded; what is missing is beneath it.

What Railway gives you to work with

a build from your repository, either through an automatic builder or your own Dockerfile.

  • Packaging: no explicit limit. The automatic builder is the constraint: it infers a runtime from your manifest and installs language packages, not system libraries.
  • Filesystem: writable and ephemeral unless a volume is attached.
  • Processes: full control. Build time and image size both cost, so a multi-hundred-megabyte browser download on every build is a running expense rather than a one-off.

The fix, if you are keeping Puppeteer

Either declare the system packages in the builder's configuration, or use your own Dockerfile. The Dockerfile is more work up front and is the version that behaves the same on every platform afterwards.

toml
# nixpacks.toml, if staying with the automatic builder.
[phases.setup]
nixPkgs = ["nodejs_20", "chromium"]

[variables]
# Use the browser the builder installed rather than downloading a
# second one into node_modules.
PUPPETEER_SKIP_DOWNLOAD = "true"
PUPPETEER_EXECUTABLE_PATH = "/root/.nix-profile/bin/chromium"

What still hurts after that

Pointing at a browser the platform installed means its version moves independently of puppeteer-core, and a mismatch shows up as a protocol error. Skipping the download keeps the image smaller and makes the browser something you no longer control. The Dockerfile route avoids both and costs you the automatic builder that was the reason for choosing this deployment style.

What Puppeteer is genuinely good at

it renders exactly what a browser renders, which means modern CSS, web fonts and client-side JavaScript all behave the way they do in a tab. That is worth keeping in view: the problem on this page is where it runs, not what it produces. Puppeteer is a Node library that drives a real Chromium, and downloads that Chromium into node_modules when it is installed. It needs the browser binary, a long list of shared libraries the browser links against, fonts, and enough memory for a browser process that is not accounted for by your runtime's heap settings.

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 Railway 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 Puppeteer run on Railway at all?

Either declare the system packages in the builder's configuration, or use your own Dockerfile. The Dockerfile is more work up front and is the version that behaves the same on every platform afterwards. 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 Railway 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.