So you have a static site in Jekyll that you want to deploy to Heroku. Lucky for you, this is a relatively easy task and does not require anything as complex as a deployment server as mentioned in the Jekyll docs. You can support this with a simple Rack script.
For those not familiar, Rack is a bare-bones web-server adapter in Ruby. Most Ruby web-frameworks sit on “top” of Rack while the Ruby web-servers sit “underneath” of it. However, we don’t need all the complexities of a web-framework to serve our static content, so we only need Rack and any Rack-compatible web-server to accomplish our goals.
The bare minimum that you’ll need are
- Jekyll site to publish
- Your favoriate text editor
- Heroku account to push to
- Ruby (recent version)
- Bundler (gem)
There are a lot of ways to install Ruby so I’ll leave this to you. I will say that I prefer rbenv for its ease of use. Once you have Ruby set up, you can install Bundler via
The first thing we need to declare is all of our runtime requirements for our Rack
script that we’ll write later. Put this in a file called
This file is rather simple, we’re stating the source of our gems (the official repo),
the Ruby version (
2.2.0) and a couple of gems. The
rack-contrib gem will pull in
some stuff for static-asset serving (as well as
rack itself) and
our Rack-compatible web-server.
Once we have our
Gemfile we need to create a
lock file. This will be used
to “lock in” specific versions of gems so that what we try out locally should be
exactly the same when we deploy it to Heroku. We can create this with
You should now have a
Gemfile.lock in your directory. Do not edit this file.
Now that we have the required gems installed, we can get down to writing our Rack
file that will serve our static assets. Create a file
config.ru with the
Let’s break this file into the important parts. The first thing we do is import the required gems. This is self explanatory. Next we see
This bit of code will enable compression on any content that is served. This will speed up your page load time, especially for those with slower internet connections. The next section is the real meat of our Rack file
This portion defines our static asset handler. The first thing it does is set the “root” of the
content directory to
_site. This means that if a request comes to
/index.html that our static
asset handler will look in
_site is the default folder for Jekyll
auto-generated content, we should be good to go here.
url defines how we want to match requests. If we wanted to have a Jekyll site as only a
sub-section of our site, this would be useful. Since, for the purposes of this example, the Jekyll
site comprises the entire web-site we can define this as
/ to mean the “root” of the web-site.
try defines various extensions to try when receiving a request. So given what we’ve seen so
far, if the application receives a request of
/awesome-article it will check for the following
files in order
With this we can make a variety of requests and get very clean URLs without having to include the
.html extensions or without having to specify
index when visiting the root of the site (e.g.
header_rules options simply allow caching to work which we defined earlier. This
should help to significantly speed up your site, especially as visitors click from page to page as
they will avoid re-loading common resources such as CSS or JS files.
This last part is especially nifty.
This will redirect any request that we can’t understand (broken link or just bad URL) back to the homepage. You could, if you wanted to be really fancy here, provide a custom “Not Found” page. Let your imagination run wild.
If we are deploying to Heroku, we need to create a
Procfile that tells Heroku how to run our site.
We can create a simple file as
This instructs Heroku to use our
puma gem to run our web-service and some additional parameters that
Heroku requires (you can safely ignore these).
We are now ready to push to Heroku! Assuming you’ve already setup your Heroku account and have authenticated in your local repo. We can run the following bit to deploy.
Personally, I like to put all of this into a Makefile sot that I can automate this process.