15 May 2012

Middleman Buildpack for Heroku

Update: Custom buildpacks aren't needed to host Middleman sites on Heroku anymore, making this blog post partially obsolete. I've updated my example of a Middleman site hosted on Heroku using the standard Ruby buildpack.

When I make a static site, Middleman is my default tool. Middleman makes building static sites as easy as Rails makes building dynamic sites. It's activately maintained by Thomas Reynolds, and keeps up with the latest changes coming out of Rails and elsewhere.

While it's an absolute pleasure to use, deploying it can be somewhat more annoying. Hosting a static site directly on S3 is an option, but it both a) costs money, and b) has extremely cumbersome permissions management. [Heroku][http://heroku.com], in contrast, is free for small sites and has fantastic permissions control. The catch is, Heroku only runs Rack-based applications.

Although Middleman ships with a Rack-compatible server, it seemed kind of silly to create a static site that gets generated over and over again with every page view. I wanted to generate my static pages once, and then just serve them directly. While it's possible to check the entire static site into git and use Rack::Static to serve each page, I wasn't really into the idea of committing build products, either.

The final solution that I settled leverages a lesser-known feature of Heroku's cedar stack, called buildpacks. Buildpacks are sort of like Heroku's deploy scripts, since they let you run arbitrary code at deploy time. My middleman buildpack automatically runs Middleman to build your static site into the ./build directory as part of the deploy process. You can create an app on Heroku using my custom buildpack by running this command:

$ heroku create --stack cedar --buildpack http://github.com/indirect/heroku-buildpack-middleman.git

Once you have the buildpack set up, you can just git push heroku master, and watch the output to see your site get built. Serving up the static pages is easy using Rack. I like to use Rack::TryStatic, since it allows nicer 404 pages. To make things as easy as possible, I've created a minimal app that is pre-configured to work with the Middleman buildpack. You can just fork that app, run heroku create, and start deploying your fully-static site to Heroku. Check out the example site, deployed to Heroku, or the app source and readme to learn more.