Environments in Jekyll

Jekyll is an awesome static-site-slash-blog generator that lets you turn a bunch of text files and a little config into a fully-fledged website ready to deploy to wherever you host that kind of thing; in fact, it's what is letting your read this post.

But Jekyll, out of the box, has no built-in support for separate working environments like you might be used to from server-side frameworks like Rails or Symfony. So with a fresh jekyll new my-awesome-blog-created site, you are essentially always working in 'production'. There's no Jekyll-provided way to exclude certain content from your build. For example, while developing your site and previewing it locally, you probably don't want to include your Google Analytics scripts, or maybe you want to use a different javascript build with more debugging output enabled.

This is just a quick post to explain how I worked around this to give myself both 'development' and 'production' environments for my Jekyll-powered site.

Update: The approach described in this post is now redundant since Jekyll actually does support evironments straight out of the box. You can find the official documentation for them here.

The simplest thing that could possibly work

The root of my solution lies in the fact that Jekyll takes its configuration - including variables you can access in your templates - from one (or more1) configuration files. By default, if you run jekyll build or jekyll serve, it will just take whatever config is in the _config.yml file in your site's root directory and use that, and the default _config.yml looks something like this:.

1# Site settings 2source: . 3destination: ./_site 4title: Peter Thompson 5email: [email protected] 6description: "Peter's awesome blog". 7baseurl: "" 8url: "http://www.petethompson.net" 9 10# Build settings 11markdown: kramdown

Given that variables added to the config file are subsequently available in our templates, the obvious thing to do is to add a new variable here to control which environment we are currently in.

To do this, you can just add a line like this to the end of your _config.yml:

1environment: development

This gives all our templates access to a new variable called site.environment and we can use this in our templates to optionally include certain content based on which environment we have set.

For example, lets say we want to include a javascript file called dev.js when we are working on our site locally, but want to replace this with prod.js in the version of the site that we deploy to production. With our new variable, we can do something like the below in our default layout file:

1<!DOCTYPE html> 2<html lang="en"> 3 {% include head.html %} 4 5 <body> 6 {% include header.html %} 7 <div class="page-content"> 8 <div class="container"> 9 {{ content }} 10 </div> 11 </div> 12 {% include footer.html %} 13 14 {% if site.environment == 'development' %} 15 <script src="{{ "/js/dev.js" | prepend: site.baseurl }}"></script> 16 {% else %} 17 <script src="{{ "/js/prod.js" | prepend: site.baseurl }}"></script> 18 {% endif %} 19 20 </body> 21</html>

With this in place, and the config as shown above, if we build our site with jekyll build we will have a script tag that references our dev.js file included in the page, and with environment set to production (or anything else) we will include prod.js instead.

A little more sophistication

The above setup is all well and good, but we still have to remember to go in and change the environment in _config.yml every time we want to deploy our site, this might be a bit inconvenient if we forget and accidentally deploy our development build or if we want to script our deployment.

Fortunately, there's an easy solution thanks to Jekyll's support for multiple config files. When you tell Jekyll to build based on multiple configs, it will take all of the config from the first file you tell it about, and then merge in the config from any subsequent config files; any new variables will be added to the base config and any duplicate variables will have their values overwitten.

This means that for our desired multi-environment setup, we can simply create a new file in our site's root directory called _config_prod.yml and redefine our environment variable in there:

1environment: production

Now, our default _config.yml will still be used when we run a plain jekyll build or jekyll serve, but whenever we want to build our production site we can point Jekyll at both of our config files like this:

1> jekyll build --config=_config.yml,_config_prod.yml 2 3Configuration file: _config.yml 4Configuration file: _config_prod.yml 5 Source: _source 6 Destination: _out 7 Generating... 8 done.

Note that we pass the names of the config files to use as a comma-separated list, it's important that there is no space between the file names and the comma, or only the first config file will be used.

And that's essentially it; you could of course, if you want, create additional config files for other environments, or override more variables than just the environment in production, but a simple 'dev mode on/off' was more than enough for my needs.

Happy blogging!

  1. This will be important later!