Adding Custom Binaries To Heroku

April 26, 2013

There are a couple of great articles out there for creating custom buildpacks. The Heroku Documentation has details on creating a Vulcan build server to package custom binaries.

In my case, I needed to add a custom image processing library and update the version of Ghost Script that is distributed via Heroku. I was seeing some weird behaviours from the version of of Ghostscript that is packaged cedar stack on Heroku that were not present in my development environment that was running newer version of Ghostscript. Rather than attempt to troubleshoot odd behaviours from out-dated software release (Heroku currently has version 8.71 installed), wouldn’t it be easier if I could simply update Ghostscript to the latest release (9.07).

In the Heroku world, adding a custom binary isn’t as simple as using bash to connect to the dyno, downloading the software and compiling. To be honest, you could to this, but every time you create a new dyno or push a new update, the binary will be gone. Luckily Heroku allows you to add custom configurations and binaries to your apps through buildpacks.

Setup Vulcan

These general instructions can also be found within the Heroku Documentation

To get started, you must first install Vulcan. I assume that you have Heroku installed an configured in your environment. Vulcan gives you the ability to compile custom binaries for your Heroku dynos.

Use gem to install Vulcan

$ gem install vulcan

Create a new build server on Heroku. Remember, Heroku does not charge for the first dyno you run and you won’t incur additional fees.

$ vulcan create vulcan-my-custom-build-server

Now you can download your software and un-tar the file. In this example, I’m downloading the latest ghost script release.

$ curl -O http://downloads.ghostscript.com/public/ghostscript-9.07.tar.gz
$ tar xzvf ghostscript-9.07.tar.gz
$ cd ghostscript-9.07

Execute the vulcan build command. This will upload the files you have just un-tared to your build server, compile the software, download a package to your local machine and also provide you with a web accessible download link from which you can also download the package.

$ vulcan build 
>> Packaging local directory
>> Uploading code for build
>> Building with: ./configure --prefix /app/vendor/ghostscript-9.07 && make install
>> Downloading build artifacts to: /tmp/ghostscript-9.07.tgz
   (available at http://vulcan-my-custom-build-server.herokuapp.com/output/56e60ba0-a7cc-11e2-9e96-0800200c9a66)

Upload to S3

You must make the custom binary that you have created available to Heroku to download when a dyno is created. The easiest way to do this is to upload the tar package to a web accessible url. Amazon S3 work well for this. Just remember to make sure the public read permissions are set for the package.

Creating a Custom Buildpack

A custom buildpack is a set of configuration settings used by Heroku whenever a dyno is created. For example, heroku-buildpack-ruby is the standard buildpack Heroku uses when creating a Ruby dyno.

A build pack consists of three files located in a /bin directory and is accessible to Heroku via a git server. To create a build pack, create a new git repo or fork the repo I’ve created for Ghostcript located here.

A buildpack contains three files in a bin directory, namely compile, detect and release.

The compile file provides instructions to download the package and unpack it. I’ve unpackaged the files to the vendor directory.

compile

echo "-----> Installing GhostScript 9.07"
cd $1
curl https://s3.amazonaws.com/clearideas-public/gs.tgz -s -O
mkdir -p vendor/gs
tar -C vendor/gs -xvf gs.tgz

The detect file simply has an echo for the binary that is being installed.

detect

echo "GhostScript 9.07"
exit 0

The release file adds the location of installed binary to Heroku’s PATH environment variable.

release

cat <<EOF

config_vars:
	PATH: $PATH:/app/vendor/gs/bin
	EOF

Referencing the Buildpack in Your Application

Now that your binary has been created, is publicly accessible via S3 and you have created instructions telling Heroku what to do with it, you must reference the buildpack in your application. I’ve used heroku-buildpack-multi. This is a custom buildpack that reads a .buildpacks file in your application’s root directory. The .buildpacks file allows your Heroku to load multiple buildpacks for your application. In my case, I’ve specified the Ghostscript buildpack I created above, as well as the default Ruby buildpack created by Heroku.

Create a .buildpacks file in the root directory of your application. Add a url for each buildpack Heroku should use. Use the appropriate Heroku buildpack for your environment.

https://github.com/clearideas/heroku-buildpack-ghostscript.git
https://github.com/heroku/heroku-buildpack-ruby.git

Tell Heroku to use heroku-buildpack-multi as the default buildpack. To do so edit the Heroku configuration setting for your application with the following command.

$ heroku config:add BUILDPACK_URL=https://github.com/ddollar/heroku-buildpack-multi.git

You may need to update references within your application to use the location of the new binary that you have installed. You should now be able to push a new update of your application and your custom binary will be installed on your dyno.

blog comments powered by Disqus