PDFPipe

Platform as a service / Railway

wkhtmltopdf on Railway: an apt package the build never runs

Command not found, at runtime, on a build whose logs show no errors at all.

What is actually wrong

The automatic builder installs language dependencies. A command line tool from the operating system is not a language dependency and nothing in the manifest mentions it, so it is never installed and the build has no reason to complain.

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 wkhtmltopdf

Declare it as a system package in the builder configuration, or switch to a Dockerfile. Note that whichever package the platform provides is the distribution build, not the patched one, so headers, footers and page numbers will be silently ignored.

toml
# nixpacks.toml
[phases.setup]
nixPkgs = ["nodejs_20", "wkhtmltopdf", "fontconfig", "liberation_ttf"]

# This installs the unpatched build. Header and footer options will
# be accepted and ignored. A Dockerfile installing the published
# package is the only way to get those.

What still hurts after that

The silent-ignore behaviour is the worst part of this combination, because the build is green, the render succeeds, the exit code is zero and the page numbers are simply absent. If the documents need running headers or page numbers, the automatic builder cannot deliver them at any configuration and a Dockerfile is mandatory.

What wkhtmltopdf is genuinely good at

it is a single binary with no runtime, so once it is present it does not care what language your application is written in. That is worth keeping in view: the problem on this page is where it runs, not what it produces. wkhtmltopdf is a command line tool built on an old fork of the Qt WebKit engine, invoked as a subprocess with an HTML file and an output path. It needs the binary itself, the X11 and font shared libraries it links against even in headless mode, and a font configuration, because it ships with no fonts of its own.

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

Declare it as a system package in the builder configuration, or switch to a Dockerfile. Note that whichever package the platform provides is the distribution build, not the patched one, so headers, footers and page numbers will be silently ignored. 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.