wkhtmltopdf on Heroku: evaluating different installation options

The easiest and most flexible way of rendering fancy PDFs is using HTML+CSS as your source. wicked_pdf and pdfkit, the most popular Rails gems, employ wkhtmltopdf under the hood to generate them. It seems that there are multiple options of running wkhtmltopdf on Heroku. This post will explore them, analyze their drawbacks and identify the most reliable one.

Method 1: Apt package

First thing that comes to mind is installing wkhtmltopdf package from Ubuntu Apt repositories. The easiest way of doing this is using apt buildpack (provided by Heroku):

1
heroku buildpacks:add --index 1 heroku-community/apt

Next, create Aptfile file in your project's root directory with the following content:

1
wkthmltopdf

Finally, push the code to Heroku.

The build phase will list all dependencies required to install wkhtmltopdf. The package depends on audio libraries, low-level USB libraries (!), X11 and GTK libs, etc. Once the deployment completes, we can test our setup:

1
heroku run bash -a YOURAPPNAME

Once the one-off dyno boots up, run:

1
2
3
4
5
6
$ wkhtmltopdf https://www.ruby-lang.org/en/ ruby.pdf
This application failed to start because it could not find or load the Qt platform plugin "xcb"
in "".

Reinstalling the application may fix this problem.
Aborted

Even though the Apt package downloaded so many dependencies, it was still not enough to successfully run wkhtmltopdf.

Even if this solution worked, it would be extremely inefficient: the application's slug size increased by 146 MB. It's crucial to keep slug size small as it affects not only deployment time but also time required to boot up any dyno (resources defined in Procfile, one-off dynos with rails console etc.).

TL;DR: apt package prohibitively increases bundle size and still doesn't work. Avoid.

Method 2: buildpack

This option provides wkhtmltopdf binary as a buildpack. There are a few different buildpacks around, the most popular (and up-to-date) being dscout/wkhtmltopdf-buildpack. It's important to note that the latest version of wkhtmltopdf is 0.12.5, while this buildpack offers at most 0.12.4.

1
2
heroku buildpacks:add https://github.com/dscout/wkhtmltopdf-buildpack.git
heroku config:set WKHTMLTOPDF_VERSION="0.12.4"

This option increases slug size by 55.6 MB.

Note for heroku-18 stack users: if your HTML source references external resources fetched over https, they will not appear on the PDF due to SSL error caused by a missing library. To resolve this issue, add apt buildpack and add the following package to your Aptfile:

1
libssl1.0.0

In total, using wkhtmltopdf buildpack with libssl increases the bundle size by 56.9 MB. This increase is passable, but not perfect. The buildpack also includes wkhtmltoimage binary. If you don't need it, you're still going to have it in your app slug, unnecessarily increasing its size.

TL;DR: fast and reliable method, with moderate slug size increase; lacks the latest version.

Method 3: Ruby gem bundling the wkhtmltopdf binary

There are three popular options:

  1. wkhtmltopdf-binary — provides version 0.12.4 for macOS, Linux 32bit and Linux 64bit. Not recommended for Heroku: the gem bundles three binaries and we need only one. Each binary is about 40 MB so the slug size would increase significantly. Fine for development.
  2. wkhtmltopdf-binary-edge — offers the latest version (0.12.5) but like the previous option, contains three binaries. Even the author recommends something else for Heroku. Good for development.
  3. wkhtmltopdf-heroku — offers a single Linux binary suitable for running on Heroku. The original gem (as of January 2019) provides version 0.12.5 but it only works on heroku-16 stack. If you're on the latest heroku-18 stack, either use the previous version (0.12.4) or check out the fork at gregnavis/wkhtmltopdf-heroku that was modified to support heroku-18.

Your Gemfile could look like this:

1
2
3
4
5
6
7
group :production do
  gem 'wkhtmltopdf-heroku'
end

group :development do
  gem 'wkhtmltopdf-binary-edge'
end

If your application is deployed on heroku-18 stack, you'll also need to add libssl1.0.0 to your Aptfile to be able to fetch external resources over https.

This method appears to be extremely size-efficient: slug size increased only by 14 MB (15.9 MB with libssl).

TL;DR: Reliable and size-efficient way offering the latest version. Recommended.

Note about binary files security

Pay particular attention when dealing with any externally-downloaded binaries. We can assume that packages downloaded from Apt repository are adequately verified. What about the buildpack? We can check in its compile phase that it downloads the files from official releases section on GitHub. That is a trusted way to download the official binary.

Finally, the gems. They merely bundle the binaries but provide no proof that they were not maliciously modified. While you should always be grateful to open source developers for their contributions, it's always a good idea to verify that the binary files are the originals provided by wkhtmltopdf maintainers. Hint: download proper .deb file (xenial for heroku-16, bionic for heroku-18), extract the package and make sure (using diff or at least openssl dgst -sha256) that the binaries are identical.

Summary

We evaluated different options of installing wkhtmltopdf on Heroku for your Ruby apps. Using wkhtmltopdf-heroku gem is the most efficient option. Before you deploy any bundled binary to production, make sure to verify that it's the original binary provided by the wkhtmltopdf maintainers.

Posted on January 9, 2019 in ruby, rails, heroku by Jacek Galanciak

comments powered by Disqus