Hyde Press

Simple • Static • Blog-aware

Jekyll Deployment Guide (Book Edition)

by Tom Preston-Werner, Nick Quaranto, Parker Moore, et al

Contents

Preface

Note: The book edition is still an early release and a work-in-progess.

This is the (official) documentation for the Jekyll static site builder / generator reformatted in a single-page book edition.

See the source repo for how the book gets auto-built with "plain" Jekyll - of course - and hosted on GitHub Pages.

Questions? Comments? Send them to the Jekyll Talk forum post titled Jekyll Docu Reformatted as a Single-Page in Black 'n' White (Book Version) - Why? Why Not?.

Onwards.

Acknowledgments

Thanks to all Jekyll contributors for making it all possible.

Part 1 - Deployment

Chapter 1.1 - GitHub Pages

GitHub Pages are public web pages for users, organizations, and repositories, that are freely hosted on GitHub’s github.io domain or on a custom domain name of your choice. GitHub Pages are powered by Jekyll behind the scenes, so in addition to supporting regular HTML content, they’re also a great way to host your Jekyll-powered website for free.

Never built a website with GitHub Pages before? See this marvelous guide by Jonathan McGlone to get you up and running. This guide will teach you what you need to know about Git, GitHub, and Jekyll to create your very own website on GitHub Pages.

Project Page URL Structure

Sometimes it’s nice to preview your Jekyll site before you push your gh-pages branch to GitHub. However, the subdirectory-like URL structure GitHub uses for Project Pages complicates the proper resolution of URLs. In order to assure your site builds properly, use site.github.url in your URL’s.

<!-- Useful for styles with static names... -->
<link href="{{ site.github.url }}/path/to/css.css" rel="stylesheet">
<!-- and for documents/pages whose URL's can change... -->
<a href="{{ page.url | prepend: site.github.url }}">{{ page.title }}</a>

This way you can preview your site locally from the site root on localhost, but when GitHub generates your pages from the gh-pages branch all the URLs will resolve properly.

Deploying Jekyll to GitHub Pages

GitHub Pages work by looking at certain branches of repositories on GitHub. There are two basic types available: user/organization pages and project pages. The way to deploy these two types of sites are nearly identical, except for a few minor details.

Use the github-pages gem

Our friends at GitHub have provided the github-pages gem which is used to manage Jekyll and its dependencies on GitHub Pages. Using it in your projects means that when you deploy your site to GitHub Pages, you will not be caught by unexpected differences between various versions of the gems. To use the currently-deployed version of the gem in your project, add the following to your Gemfile:

source 'https://rubygems.org'

require 'json'
require 'open-uri'
versions = JSON.parse(open('https://pages.github.com/versions.json').read)

gem 'github-pages', versions['github-pages']
This will ensure that when you run bundle install, you have the correct version of the github-pages gem. If that fails, simplify it:
source 'https://rubygems.org'

gem 'github-pages'
And be sure to run bundle update often.

User and Organization Pages

User and organization pages live in a special GitHub repository dedicated to only the GitHub Pages files. This repository must be named after the account name. For example, @mojombo’s user page repository has the name mojombo.github.io.

Content from the master branch of your repository will be used to build and publish the GitHub Pages site, so make sure your Jekyll site is stored there.

Custom domains do not affect repository names

GitHub Pages are initially configured to live under the username.github.io subdomain, which is why repositories must be named this way even if a custom domain is being used.

Project Pages

Unlike user and organization Pages, Project Pages are kept in the same repository as the project they are for, except that the website content is stored in a specially named gh-pages branch. The content of this branch will be rendered using Jekyll, and the output will become available under a subpath of your user pages subdomain, such as username.github.io/project (unless a custom domain is specified—see below).

The Jekyll project repository itself is a perfect example of this branch structure—the master branch contains the actual software project for Jekyll, however the Jekyll website (that you’re looking at right now) is contained in the gh-pages branch of the same repository.

Source Files Must be in the Root Directory

GitHub Pages overrides the “Site Source” configuration value, so if you locate your files anywhere other than the root directory, your site may not build correctly.

GitHub Pages Documentation, Help, and Support

For more information about what you can do with GitHub Pages, as well as for troubleshooting guides, you should check out GitHub’s Pages Help section. If all else fails, you should contact GitHub Support.

Chapter 1.2 - Deployment Methods

Sites built using Jekyll can be deployed in a large number of ways due to the static nature of the generated output. A few of the most common deployment techniques are described below.

Web hosting providers (FTP)

Just about any traditional web hosting provider will let you upload files to their servers over FTP. To upload a Jekyll site to a web host using FTP, simply run the jekyll command and copy the generated _site folder to the root folder of your hosting account. This is most likely to be the httpdocs or public_html folder on most hosting providers.

FTP using Glynn

There is a project called Glynn, which lets you easily generate your Jekyll powered website’s static files and send them to your host through FTP.

Self-managed web server

If you have direct access yourself to the deployment web server yourself, the process is essentially the same, except you might have other methods available to you (such as scp, or even direct filesystem access) for transferring the files. Just remember to make sure the contents of the generated _site folder get placed in the appropriate web root directory for your web server.

Automated methods

There are also a number of ways to easily automate the deployment of a Jekyll site. If you’ve got another method that isn’t listed below, we’d love it if you contributed so that everyone else can benefit too.

Git post-update hook

If you store your Jekyll site in Git (you are using version control, right?), it’s pretty easy to automate the deployment process by setting up a post-update hook in your Git repository, like this.

Git post-receive hook

To have a remote server handle the deploy for you every time you push changes using Git, you can create a user account which has all the public keys that are authorized to deploy in its authorized_keys file. With that in place, setting up the post-receive hook is done as follows:

laptop$ ssh deployer@example.com
server$ mkdir myrepo.git
server$ cd myrepo.git
server$ git --bare init
server$ cp hooks/post-receive.sample hooks/post-receive
server$ mkdir /var/www/myrepo

Next, add the following lines to hooks/post-receive and be sure Jekyll is installed on the server:

GIT_REPO=$HOME/myrepo.git
TMP_GIT_CLONE=$HOME/tmp/myrepo
PUBLIC_WWW=/var/www/myrepo

git clone $GIT_REPO $TMP_GIT_CLONE
jekyll build -s $TMP_GIT_CLONE -d $PUBLIC_WWW
rm -Rf $TMP_GIT_CLONE
exit

Finally, run the following command on any users laptop that needs to be able to deploy using this hook:

laptops$ git remote add deploy deployer@example.com:~/myrepo.git

Deploying is now as easy as telling nginx or Apache to look at /var/www/myrepo and running the following:

laptops$ git push deploy master

Jekyll-hook

You can also use jekyll-hook, a server that listens for webhook posts from GitHub, generates a website with Jekyll, and moves it somewhere to be published. Use this to run your own GitHub Pages-style web server.

This method is useful if you need to serve your websites behind a firewall, need extra server-level features like HTTP basic authentication or want to host your site directly on a CDN or file host like S3.

Setup steps are fully documented in the jekyll-hook repo.

Static Publisher

Static Publisher is another automated deployment option with a server listening for webhook posts, though it’s not tied to GitHub specifically. It has a one-click deploy to Heroku, it can watch multiple projects from one server, it has an easy to user admin interface and can publish to either S3 or to a git repository (e.g. gh-pages).

Rake

Another way to deploy your Jekyll site is to use Rake, HighLine, and Net::SSH. A more complex example of deploying Jekyll with Rake that deals with multiple branches can be found in Git Ready.

scp

Once you’ve generated the _site directory, you can easily scp it using a tasks/deploy shell script similar to this deploy script here. You’d obviously need to change the values to reflect your site’s details. There is even a matching TextMate command that will help you run this script from within Textmate.

rsync

Once you’ve generated the _site directory, you can easily rsync it using a tasks/deploy shell script similar to this deploy script here. You’d obviously need to change the values to reflect your site’s details.

Certificate-based authorization is another way to simplify the publishing process. It makes sense to restrict rsync access only to the directory which it is supposed to sync. This can be done using rrsync.

Step 1: Install rrsync to your home folder (server-side)

If it is not already installed by your host, you can do it yourself:

Step 2: Set up certificate-based SSH access (server side)

This process is described in several places online. What is different from the typical approach is to put the restriction to certificate-based authorization in ~/.ssh/authorized_keys. Then, launch rrsync and supply it with the folder it shall have read-write access to:

command="$HOME/bin/rrsync <folder>",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding ssh-rsa <cert>

<folder> is the path to your site. E.g., ~/public_html/you.org/blog-html/.

Step 3: Rsync (client-side)

Add the deploy script to the site source folder:

#!/bin/sh

rsync -crvz --rsh=ssh -p2222' --delete-after --delete-excluded   <folder> <user>@<site>:

Command line parameters are:

Using this setup, you might run the following command:

rsync -crvz --rsh='ssh -p2222' --delete-after --delete-excluded   _site/ hostuser@example.org:

Don’t forget the column : after server name!

Step 4 (Optional): Exclude the transfer script from being copied to the output folder.

This step is recommended if you use these instructions to deploy your site. If you put the deploy script in the root folder of your project, Jekyll will copy it to the output folder. This behavior can be changed in _config.yml.

Just add the following line:

# Do not copy these files to the output directory
exclude: ["deploy"]

Alternatively, you can use an rsync-exclude.txt file to control which files will be transferred to your server.

Done!

Now it’s possible to publish your website simply by running the deploy script. If your SSH certificate is passphrase-protected, you will be asked to enter it when the script executes.

Rack-Jekyll

Rack-Jekyll is an easy way to deploy your site on any Rack server such as Amazon EC2, Slicehost, Heroku, and so forth. It also can run with shotgun, rackup, mongrel, unicorn, and others.

Read this post on how to deploy to Heroku using Rack-Jekyll.

Jekyll-Admin for Rails

If you want to maintain Jekyll inside your existing Rails app, Jekyll-Admin contains drop in code to make this possible. See Jekyll-Admin’s README for more details.

Amazon S3

If you want to host your site in Amazon S3, you can do so by using the s3_website application. It will push your site to Amazon S3 where it can be served like any web server, dynamically scaling to almost unlimited traffic. This approach has the benefit of being about the cheapest hosting option available for low-volume blogs as you only pay for what you use.

OpenShift

If you’d like to deploy your site to an OpenShift gear, there’s a cartridge for that.

ProTip™: Use GitHub Pages for zero-hassle Jekyll hosting

GitHub Pages are powered by Jekyll behind the scenes, so if you’re looking for a zero-hassle, zero-cost solution, GitHub Pages are a great way to host your Jekyll-powered website for free.

Kickster

Use Kickster for easy (automated) deploys to GitHub Pages when using unsupported plugins on GitHub Pages.

Kickster provides a basic Jekyll project setup packed with web best practises and useful optimization tools increasing your overall project quality. Kickster ships with automated and worry-free deployment scripts for GitHub Pages.

Setting up Kickster is very easy, just install the gem and you are good to go. More documentation can here found here. If you do not want to use the gem or start a new project you can just copy paste the deployment scripts for Travis CI or Circle CI.

Chapter 1.3 - Continuous Integration

You can easily test your website build against one or more versions of Ruby. The following guide will show you how to set up a free build environment on Travis, with GitHub integration for pull requests. Paid alternatives exist for private repositories.

1. Enabling Travis and GitHub

Enabling Travis builds for your GitHub repository is pretty simple:

  1. Go to your profile on travis-ci.org: https://travis-ci.org/profile/username
  2. Find the repository for which you’re interested in enabling builds.
  3. Click the slider on the right so it says “ON” and is a dark grey.
  4. Optionally configure the build by clicking on the wrench icon. Further configuration happens in your .travis.yml file. More details on that below.

2. The Test Script

The simplest test script simply runs jekyll build and ensures that Jekyll doesn’t fail to build the site. It doesn’t check the resulting site, but it does ensure things are built properly.

When testing Jekyll output, there is no better tool than html-proofer. This tool checks your resulting site to ensure all links and images exist. Utilize it either with the convenient htmlproof command-line executable, or write a Ruby script which utilizes the gem.

Save the commands you want to run and succeed in a file: ./script/cibuild

The HTML Proofer Executable

#!/usr/bin/env bash
set -e # halt script on error

bundle exec jekyll build
bundle exec htmlproof ./_site

Some options can be specified via command-line switches. Check out the html-proofer README for more information about these switches, or run htmlproof --help locally.

For example to avoid testing external sites, use this command:

$ bundle exec htmlproof ./_site --disable-external

The HTML Proofer Library

You can also invoke html-proofer in Ruby scripts (e.g. in a Rakefile):

#!/usr/bin/env ruby

require 'html/proofer'
HTML::Proofer.new("./_site").run

Options are given as a second argument to .new, and are encoded in a symbol-keyed Ruby Hash. For more information about the configuration options, check out html-proofer’s README file.

3. Configuring Your Travis Builds

This file is used to configure your Travis builds. Because Jekyll is built with Ruby and requires RubyGems to install, we use the Ruby language build environment. Below is a sample .travis.yml file, followed by an explanation of each line.

Note: You will need a Gemfile as well, Travis will automatically install the dependencies based on the referenced gems:

source "https://rubygems.org"

gem "jekyll"
gem "html-proofer"

Your .travis.yml file should look like this:

language: ruby
rvm:
- 2.1

before_script:
 - chmod +x ./script/cibuild # or do this locally and commit

# Assume bundler is being used, therefore
# the `install` step will run `bundle install` by default.
script: ./script/cibuild

# branch whitelist, only for GitHub Pages
branches:
  only:
  - gh-pages     # test the gh-pages branch
  - /pages-(.*)/ # test every branch which starts with "pages-"

env:
  global:
  - NOKOGIRI_USE_SYSTEM_LIBRARIES=true # speeds up installation of html-proofer

Ok, now for an explanation of each line:

language: ruby

This line tells Travis to use a Ruby build container. It gives your script access to Bundler, RubyGems, and a Ruby runtime.

rvm:
- 2.1

RVM is a popular Ruby Version Manager (like rbenv, chruby, etc). This directive tells Travis the Ruby version to use when running your test script.

before_script:
 - chmod +x ./script/cibuild

The build script file needs to have the executable attribute set or Travis will fail with a permission denied error. You can also run this locally and commit the permissions directly, thus rendering this step irrelevant.

script: ./script/cibuild

Travis allows you to run any arbitrary shell script to test your site. One convention is to put all scripts for your project in the script directory, and to call your test script cibuild. This line is completely customizable. If your script won’t change much, you can write your test incantation here directly:

install: gem install jekyll html-proofer
script: jekyll build && htmlproof ./_site

The script directive can be absolutely any valid shell command.

# branch whitelist, only for GitHub Pages
branches:
  only:
  - gh-pages     # test the gh-pages branch
  - /pages-(.*)/ # test every branch which starts with "pages-"

You want to ensure the Travis builds for your site are being run only on the branch or branches which contain your site. One means of ensuring this isolation is including a branch whitelist in your Travis configuration file. By specifying the gh-pages branch, you will ensure the associated test script (discussed above) is only executed on site branches. If you use a pull request flow for proposing changes, you may wish to enforce a convention for your builds such that all branches containing edits are prefixed, exemplified above with the /pages-(.*)/ regular expression.

The branches directive is completely optional. Travis will build from every push to any branch of your repo if leave it out.

env:
  global:
  - NOKOGIRI_USE_SYSTEM_LIBRARIES=true # speeds up installation of html-proofer

Using html-proofer? You’ll want this environment variable. Nokogiri, used to parse HTML files in your compiled site, comes bundled with libraries which it must compile each time it is installed. Luckily, you can dramatically decrease the install time of Nokogiri by setting the environment variable NOKOGIRI_USE_SYSTEM_LIBRARIES to true.

Be sure to exclude vendor from your _config.yml

Travis bundles all gems in the vendor directory on its build servers, which Jekyll will mistakenly read and explode on.

exclude: [vendor]

Troubleshooting

Travis error: “You are trying to install in deployment mode after changing your Gemfile. Run bundle install elsewhere and add the updated Gemfile.lock to version control.”

Workaround: Either run bundle install locally and commit your changes to Gemfile.lock, or remove the Gemfile.lock file from your repository and add an entry in the .gitignore file to avoid it from being checked in again.

Questions?

This entire guide is open-source. Go ahead and edit it if you have a fix or ask for help if you run into trouble and need some help.