Building and Deploying Custom Binaries on Heroku

I recently needed to deploy an upgraded binary of ghostscript onto Heroku. I’m not the first person to need version 9 but I didn’t see very much in the way of polished solutions. Below is a solution I put together for getting any custom binary on Heroku.

Heroku’s vulcan build server is a great start, and (although I had some trouble with SSH keys) it can help you get the binaries you need. However, it’s only a small piece of the puzzle since it doesn’t help you deploy it.

What You’ll Need

To do what I did, you’ll need a few things:

  • An existing and running Heroku application. This is the machine you’ll build the new binary on.
  • A public web server, such as Amazon S3. You’ll host the final binary here, and it will be installed with Peter Keen’s vendorbinaries buildpack. This will be needed permanently every time you deploy.
  • A public server. You’ll use this to get the binary off of Heroku and onto somewhere less transient using SCP, FTP, or a similar file transfer tool. This will be needed only when you build.

Build the Binary

Step one is to get build something you can deploy. I followed essentially the same process as Trung Lê‘s instructions in Ruby Journal to build ghostscript:

  1. Use heroku run bash to get a shell running on Heroku.
  2. Use curl to download the source code.
  3. Configure the package: ./configure --prefix=/my/new/software. It’s important that you install this in a custom location (with the --prefix option) so that you have a clean directory that doesn’t include any other files.
  4. Build it: make install.
  5. This is where we’ll deviate from Trung’s instructions. Since we don’t care about the installation on the current dyno (we have a longer-term view), our next step is to get the files off this machine. Tar up the installed binary (take note of the relative path; the untar will take place in your app’s root, so setting it up like this will ensure things get laid out well in the end): cd /my/new/software; tar cvzf /tmp/my-software-1.0-bin.tar.gz .
  6. Transfer it to your ssh server: scp /tmp/my-software-1.0-bin.tar.gz my-server:

Now you have a working installed binary ready to be deployed.

Host the Binary

Your custom binary needs to be somewhere publicly accessible. The vendorbinaries buildpack is going to pull down the compiled binary on each deploy, so Heroku needs to be able to access the URL via HTTP. Amazon S3 is a good place to upload it to.

Set Up the Buildpack

Heroku’s Cedar stack and custom buildpacks provides an extremely powerful platform, but the common case doesn’t need that complexity. Instead of trying to create a custom buildpack or forking one of the stock ones, you can now use the ‘multi’ buildpack to install multiple buildpacks on top of one another, allowing you to take advantage of what’s already in place but still letting you add your own customizations.

  1. First, configure heroku to use the multi pack: heroku config:set BUILDPACK_URL=https://github.com/ddollar/heroku-buildpack-multi.git
  2. Tell it what buildpacks to use by creating a file in the root of you application named .buildpacks. Add one URL per line with the buildpacks you require. They will be installed in the order they are listed. For me, the list was:
    • https://github.com/peterkeen/heroku-buildpack-vendorbinaries.git (The buildpack that will download our custom binary and install it.)
    • https://github.com/heroku/heroku-buildpack-ruby.git (The default Heroku Ruby buildpack.)
  3. Tell the vendor-binaries buildpack where to download the binaries from by creating a file in your application’s root named .vendor_urls. Give it one URL per tarball you need.

During the deploy process, Heroku will download and untar the binaries from the root of your application.

Configure Your Path

If your custom binaries are not intended to install in your application’s bin/ directory, you may need to ensure they’re in your PATH with a command like: heroku config:set PATH=vendor/MY-APP/bin:bin:vendor/bundle/ruby/1.9.1/bin:/usr/local/bin:/usr/bin:/bin. You should put your new tool’s path at the beginning of the PATH variable to avoid possible conflicts with already installed tools.

Deploy

Most configuration changes on Heroku take place immediately, but the change to the buildpack we made won’t do anything until the next time you deploy. Push your code, you’ll see each buildpack run in turn, and your new custom binary should be available.