PDFPipe

Platform as a service / Heroku

wkhtmltopdf on Heroku: a binary with nowhere to live

Locally it works because the tool is installed on your machine. On a dyno, the command is not found.

What is actually wrong

There is no package manager at runtime and the slug contains only what the buildpacks put there. A tool installed on a development machine by the operating system is simply absent, and the failure is a missing command rather than anything to do with rendering.

What Heroku gives you to work with

a slug built by buildpacks from your repository, run on a dyno with an ephemeral filesystem.

  • Packaging: the compiled slug has a 500 MB limit. A browser binary plus its shared libraries is a large fraction of that before your application is counted.
  • Filesystem: writable but ephemeral, and reset on every dyno restart, which happens at least daily. Nothing written at runtime survives.
  • Processes: a child process can be spawned, and it competes for the dyno's memory allowance. Exceeding that allowance produces a memory quota error in the logs rather than a crash, and the dyno keeps running while swapping.

The fix, if you are keeping wkhtmltopdf

A buildpack that vendors the binary and its shared libraries into the slug and puts them on the path. Fonts have to come with it, because the base image has none and this tool ships none.

bash
heroku buildpacks:add --index 1 https://github.com/dscout/wkhtmltopdf-buildpack

# The binary lands on the path. Fonts do not come with it: an apt
# buildpack with an Aptfile is the usual way to add them.
#   echo "fonts-liberation" >> Aptfile
#   heroku buildpacks:add --index 1 heroku-community/apt

What still hurts after that

You are now depending on two community buildpacks, both of which pin versions that were current when they were written, and neither of which is on your release schedule. Each dyno restart re-extracts the slug, so there is no warm state to lose but also none to gain. And the underlying engine limitation is unchanged: modern CSS does not render the way it does in the browser the template was designed in.

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

A buildpack that vendors the binary and its shared libraries into the slug and puts them on the path. Fonts have to come with it, because the base image has none and this tool ships none. 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 Heroku 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.