Jekyll on Heroku

Listen to this article

Jekyll, the static website generator written in Ruby and popularized by GitHub, is a great candidate for being run on Heroku. Originally built to run on GitHub Pages, running Jekyll on Heroku allows you to take advantage of Jekyll’s powerful plugin system to do more than convert Markdown to HTML. On my blog, I have plugins to download my Goodreads current and recently read books and to generate Open Graph images for posts. That said, it’s not straightforward to get up and running on Heroku without using jekyll serve to do the heavy lifting. jekyll serve uses Ruby’s built-in, single threaded web server WEBrick, but a public site should be using a web server more suited for production, like nginx.

We’ll start from the very beginning. You’ll need Ruby and Bundler installed.

I like ruby-install and chruby as my Ruby installer and switcher.

This is the platform-agnostic way of installing the latest version of ruby-install. If you prefer, you can find instructions for OS X and Windows in ruby-install’s README.

The commands below use the most recent versions of ruby-install and chruby available when this post was published. You probably want to use more recent versions if available.

$ wget -O ruby-install-0.6.1.tar.gz https://github.com/postmodern/ruby-install/archive/v0.6.1.tar.gz $ tar -xzvf ruby-install-0.6.1.tar.gz $ cd ruby-install-0.6.1/ $ sudo make install

You’ll also want chruby, which you can similarly install in a more platform specific way using instructions in chruby’s README.

$ wget -O chruby-0.3.9.tar.gz https://github.com/postmodern/chruby/archive/v0.3.9.tar.gz $ tar -xzvf chruby-0.3.9.tar.gz $ cd chruby-0.3.9/ $ sudo make install

Install the latest Ruby and use it:

$ ruby-install ruby $ chruby ruby

You’ll need Bundler and the Jekyll gem:

$ gem install bundler jekyll Fetching: bundler-1.9.9.gem (100%) Successfully installed bundler-1.9.9 Fetching: jekyll-3.6.2.gem (100%) Successfully installed jekyll-3.6.2 2 gems installed

Generating a new jekyll site is straightforward, and you get a site template for free similar to starting a new Rails app. We’ll be saving our work in Git along the way:

$ jekyll new jekyll-on-heroku Ignoring nokogiri-1.8.1 because its extensions are not built. Try: gem pristine nokogiri --version 1.8.1 Running bundle install in /Users/caleb.thompson/code/jekyll-on-heroku/_rundoc/tmp/jekyll-on-heroku... … New jekyll site installed in /Users/caleb.thompson/code/jekyll-on-heroku/_rundoc/tmp/jekyll-on-heroku. $ cd jekyll-on-heroku $ git init Initialized empty Git repository in /Users/caleb.thompson/code/jekyll-on-heroku/_rundoc/tmp/jekyll-on-heroku/.git/ $ git add . $ git commit -m "jekyll new jekyll-on-heroku" [master (root-commit) ea8cd80] jekyll new jekyll-on-heroku 8 files changed, 206 insertions(+) create mode 100644 .gitignore create mode 100644 404.html create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 _config.yml create mode 100644 _posts/2017-12-07-welcome-to-jekyll.markdown create mode 100644 about.md create mode 100644 index.md

We have our Jekyll site (go ahead and try jekyll serve if you’d like), but we’ll need a Heroku app to deploy to. You will need an account and the CLI installed before this step. We’re going to use the official Ruby buildpack as well as Heroku’s (unofficial) Static buildpack to do the site generation and static page serving, respectively:

$ heroku create Creating app... done, radiant-bastion-50307 https://radiant-bastion-50307.herokuapp.com/ | https://git.heroku.com/radiant-bastion-50307.git $ heroku buildpacks:add heroku/ruby Buildpack added. Next release on radiant-bastion-50307 will use heroku/ruby. Run git push heroku master to create a new release using this buildpack. $ heroku buildpacks:add https://github.com/heroku/heroku-buildpack-static Buildpack added. Next release on radiant-bastion-50307 will use: 1. heroku/ruby 2. https://github.com/heroku/heroku-buildpack-static Run git push heroku master to create a new release using these buildpacks.

Heroku’s buildpacks need specific files to be present to work correctly. We already have Gemfile for the Ruby buildpack, but we’ll also need a static.json file for the static buildpack. I’ve added a few of my favorite options to the static.json , and we’ll need to use _site/ for our root as that is where Jekyll builds its static content by default.

In file static.json write:

{ "clean_urls": true, "https_only": true, "root": "_site/" }

Let’s save our progress again.

$ git add static.json $ git commit -m "Add static.json with reasonable defaults" [master 6e1dea7] Add static.json with reasonable defaults 1 file changed, 5 insertions(+) create mode 100644 static.json

The Ruby buildpack runs rake assets:precompile when it’s available. This is inspired by Rails’ pattern, but it works for any assets you’ll need. We’re going to use it for our entire site by running jekyll build .

In file Rakefile write:

task "assets:precompile" do exec("jekyll build") end

You’ll need Rake to run this in the first place, and we can save ourselves trouble in the future by specifying a version.

At the end of Gemfile add:

gem "rake" ruby "2.4.2"

And run bundle to install Rake.

$ bundle … Bundle complete! 5 Gemfile dependencies, 24 gems now installed. Use `bundle info [gemname]` to see where a bundled gem is installed.

We need to add the changes for the Rake task and new dependencies:

$ git add Rakefile Gemfile Gemfile.lock $ git commit -m "Add Rake task to build Jekyll site" [master 94f664b] Add Rake task to build Jekyll site 3 files changed, 10 insertions(+) create mode 100644 Rakefile

Finally, we can push the app to Heroku!

$ git push heroku master warning: not sending a push certificate since the receiving end does not support --signed push remote: Compressing source files... done. remote: Building source: remote: remote: -----> Ruby app detected remote: -----> Compiling Ruby remote: -----> Using Ruby version: ruby-2.4.2 remote: -----> Installing dependencies using bundler 1.15.2 remote: Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment … remote: -----> Detecting rake tasks remote: -----> Precompiling assets remote: Running: rake assets:precompile remote: Configuration file: /tmp/build_c29844d07a39f5ce14fd64c34fc17680/_config.yml remote: Source: /tmp/build_c29844d07a39f5ce14fd64c34fc17680 remote: Destination: /tmp/build_c29844d07a39f5ce14fd64c34fc17680/_site remote: Incremental build: disabled. Enable with --incremental remote: Generating... remote: done in 0.901 seconds. remote: Auto-regeneration: disabled. Use --watch to enable. remote: Asset precompilation completed (1.49s) remote: remote: ###### WARNING: remote: No Procfile detected, using the default web server. remote: We recommend explicitly declaring how to boot your server process via a Procfile. remote: https://devcenter.heroku.com/articles/ruby-default-web-server remote: remote: -----> Static HTML app detected remote: % Total % Received % Xferd Average Speed Time Time Time Current remote: 100 838k 100 838k 0 0 5598k 0 --:--:-- --:--:-- --:--:-- 5628k remote: -----> Installed directory to /app/bin remote: -----> Discovering process types remote: Procfile declares types -> (none) remote: Default types for buildpack -> web remote: remote: -----> Compressing... remote: Done: 26.4M remote: -----> Launching... remote: Released v3 remote: https://radiant-bastion-50307.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy... done. To https://git.heroku.com/radiant-bastion-50307.git * [new branch] master -> master

Open the URL at the end and enjoy your new Jekyll app on Heroku!