Dockerize Your Octopress 3.0 Site With Heroku
Why don’t I just blog on Medium with all the cool kids? I have several responses. First, I’m a control freak and am only moderately repentant about it. I want to make my own website, not borrow space on somebody else’s. Second, I like writing code, and the Octopress/Jekyll platform makes me feel like a proper coder even when I’m just blogging. Those two items make the extra work worth it to me. And yet there is a third motivator that is more to the point of this post: I get an opportunity to play with Docker, a technology I have grown to love, and to boot, I get to continue my love affair with Heroku, my favored PaaS. It just so happens that a Dockerized website is not only fun, but also useful. Hence, I’m motivated to write this post detailing both the why and the how, especially since I had to fumble around a bit to get this to work right and thought others attempting this feat might benefit from my experience.
I may be a control freak who wants to make his own website - albeit with the help of a well-done publicly available Jekyll theme to compensate for my Drudge Report design skills. But configuring my own web server box and keeping it up to date? Ain’t nobody got time for that - not for a personal website with no specific readership ambitions anyways. Heroku allows me to provide some fairly trivial configuration, code my code, git push, and be done with it.
Since this is a static site with no backend components, there is a case to be made that Heroku is a wee bit of overkill. Using an AWS S3 bucket could end up being similarly convenient, cheaper, and might even result in a faster website. That may well be something I look into in the near future. But for now, Heroku is what I know and can work with without a learning curve, so Heroku wins the day. I wouldn’t necessarily say Heroku is superior to any other PaaS provider - it’s just the one I’ve used in a couple professional settings and have hence grown accustomed to. I tend to go with familiarity, all else being equal.
Containerization via Docker yields all kinds of benefits for deploying and running software components at scale, many of which don’t apply here and could be better articulated by people who know better. My work here keys on one particular benefit: a predictable and repeatable execution environment. By encapsulating my website into a runnable, distributable unit that runs on my local machine just as well as on Heroku, I can be confident that the site I’m producing locally is precisely what will run in production.
Granted, for something like an Octopress site there isn’t a whole lot of complexity to contend with, and I would not expect to see production-only bugs in this context. If nothing else, consider this a low-risk, low-impact way to give the Docker toolset a spin along with a prominent PaaS provider. Use a free dyno if you don’t care about 24/7 uptime.
With all that said, let’s get down to brass tacks.
Adapt Your Octopress 3.0 Site to Run on Heroku
Before we check out the Docker toolset, there are a couple of tweaks that are helpful, and in one case outright required, in order for your Octopress site to run as a Heroku Ruby application.
First, the Heroku Ruby buildpack expects to run
rake assets:precompile as part of the build process. If this were a Rails 3+ application, you’d have it out of the box. Here in non-Rails land, we have two options. We could simply run
jekyll build and check in the generated site output (aka the
_site folder), and then provide a no-op
assets:precompile in a Rakefile. Personally, I dislike checking in generated code that I didn’t directly write if I can help it. The diffs become less readable due to being bloated with code that isn’t really meant to be evaluated by human reviewers.
Besides, we have a second option: we can put
_site in .gitignore, roll our own
assets:precompile rake task, and have the buildpack generate our site output for us.
Given the second option, here’s what your .gitignore could look like:
Now for a Rakefile that performs a
jekyll build as a stand-in for
First, let’s start with a Gemfile that looks like this:
We’ll want a
Procfile that declares the command necessary to kick off our webserver:
Just where are we initializing all those environment variables? Two places, depending on context. For local development, provide a
For production, you’ll want to set these variables in the “Config Variables” section of your Heroku application settings.
At this point, you’ll be able to plain-vanilla deploy to Heroku with git. The bundle will install, your site will build, and you’ll have it live within a minute or so.
Now let’s take a look at how we can use Docker to obtain a consistent execution environment between local and production.
First, a suggestion. If you’re working from OSX or Windows, you’ll notice Docker provides you with docker-machine to enable you to run Docker processes without having to explicitly access a Linux environment. Feel free to use it if you feel up to it. In my personal experience, in addition to what has been relayed to me by colleagues, docker-machine is not particularly reliable. In Docker’s defense, they’re working on it. Until then, if you have to involve VirtualBox anyways, then you might as well just work with Docker in an explicit Linux environment. Use Vagrant to accomplish this. If Ubuntu suits you, feel free to use my Vagrant setup.
This post would get awfully long were I to go into the fundamentals of constructing a Docker image. Thankfully, Heroku does us another solid by providing a plugin and other tooling that will give us pretty much everything we’ll need.
Given we have the toolbelt and the Docker plugin installed:
We can bootstrap ourselves for Docker execution like so:
That adds a
Dockerfile and a
Dockerfile need not change at all - Heroku’s base ruby image will serve us perfectly well and encapsulates the same Cedar stack setup that runs in production. Note that Ruby 2.2.3 is required, hence the Gemfile directive.
I do propose you slightly alter your
Because we have a handy-dandy
.env file for local environment variables, there’s no need to declare environment variables in our
.env to help us here, we’ll need to use
foreman start as our web command.
Also, note one of the many benefits of docker-compose: defining multiple processes for a single container, in this case a bash shell into our container. So far this has been a YAGNI for me, but your mileage may vary.
One more piece we’ll need is an
app.json file to provide some necessary information for the
And boom, Dockerized.
Run It Locally, Docker Style
Given the above, we can build our site inside of the
heroku/ruby container and then kick off a web process to serve up our site locally.
http://localhost:4000 in a browser and behold your website in a production-like local context.
Deploy It, Docker Style
heroku-docker plugin also allows us to deploy and execute our site in production, using the very same Docker setup we just used locally.
Boom, deployed to production. And what’s more, you can be sure that you’re deploying exactly what you just ran locally.
Just like that, we have an Octopress/Jekyll site Dockerized and running on Heroku. Overkill for a fairly simple personal website? Perhaps. But as aforementioned, an inconsequential personal website is a great opportunity to get your feet wet with these tools, precisely because you might be the only one who cares if it goes poorly. Fortunately for me, it went swimmingly, and now all three of you readers of this blog are experiencing the awesomeness of a Dockerized static site. Can you feel it? Does the fresh Docker aura not just emanate from the screen?
Well, it was fun for me anyways.