Explaining Heroku Custom Buildpacks

Web Dynos in the Heroku Cedar Stack are virtual servers with 500MB RAM and through the use of Buildpacks, these Web Dynos can run most anything. Heroku supports many popular languages through their Default Buildpacks and if that’s not good enough, Custom Buildpacks expand your options significantly.

Given a recent requirement to serve a one-page Javascript app (static HTML/CSS/JS), I knew the awesome Nginx web server would be a good fit and given the effective scaling, load balancing and failover the Cedar Stack provides, I figured a Custom Nginx Buildpack would be a good choice.

Starting with whatever documentation and sample Buildpacks I could find, I eventually succeeded but not without my share of trial and error. It really helps to understand the big picture as well as the details so that’s what I’m going to document here.

tl;dr

  1. Create your Buildpack and store it somewhere (Github) so Heroku can grab it each time you deploy your app. (The Buildpack orchestrates the Slug Compilation Process.)
  2. Make a Vulcan Build Server to build and package your code (Nginx in my case) and store it somewhere (S3) so Heroku can grab it and bake it into your Slug whenever you deploy your app. You may want to use a shell script to package and stage your binaries.
  3. For each app that’ll use your Buildpack, set the BUILDPACK_URL config var.

That’s it. When you deploy your app to Heroku, your Custom Buildpack will create a Slug for your app that’ll run on how ever many Web Dynos your workload requires.

Anatomy of a Buildpack

The only Buildpack requirements are these three bash scripts:

  • bin/detect – Looks for a trigger in the app to determine Buildpack applicability.
  • bin/compile – Controls the Slug Compilation process.
  • bin/release – Let’s you set default PATH and other meta data.

As you examine other Buildpacks, you’ll notice many with additional files. In my case, I felt my Buildpack was the right place to keep my Vulcan Build shell script and my Nginx configuration files. (If you go that route, don’t miss the note below explaining how to access files contained within your Buildpack at Slug Compilation time.)

What’s Vulcan for?

It’s a build server (check out the gem) that simplifies the process of compiling your app dependencies (for me, that’s Nginx and PCRE) on the target environment (a Heroku Web Dyno.)

The devil is in the details

  • Make sure your Heroku Account is Verified (CC on file.) Why? Because Vulcan has a dependency on the Heroku Cloudant Addon and that Addon won’t install itself if your Account isn’t Verified. (Github Issue. Thanks @ddollar!)
  • When you’re compiling your Slug, (while running bin/compile) the location of your Buildpack is available at $(dirname $0) so if you want to reference files within your Buildpack, you may want to use something like:
    BUILDPACK_DIR=`cd $(dirname $0); cd ..; pwd`. For an example, check out my bin/compile script. (Thanks again @ddollar!)

Summary: Custom Buildpacks FTW!

I hope this helps you take advantage of this incredibly powerful Heroku feature. I’ve been appreciating Heroku more and more and coming to understand Buildpacks has practically turned me into a Heroku fanboy. When we get around to putting our app into production, I’ll do some Nginx performance tuning and will try to remember to post my load test findings. I don’t expect price performance to match bare metal or dedicated virtual servers but considering the features Heroku delivers, I do expect a reasonable ROI.