Setting up Jekyll - the basics


Since Octopress has now been deprecated for pure use of Jekyll, this blog has now been switched to use exactly that. See this post as a walkthrough on creating a functional blog via Jekyll (part 1). This is yet another collation of snippets I have found useful, to be used as a reference for my future self - and maybe others.

Starting off

You can find the installation instructions for Jekyll in the official documentation.

After the Jekyll gem has been installed scaffolding a new site is as easy as

jekyll new blog_name

This will create a new folder with a starter site. To serve your site to preview it, run the

jekyll --serve

command and navigate to http://localhost:4000 (the default port) with your favourite browser.

Defaults, excerpts and URL structures

By default the home page will display the full list of posts with all it's glorious content. This might be a bit too much for longer posts. In order to enable excerpts with links to the full post you need to configure the excerpt_seperator. This can be specified in the front matter of each post. We can also specify the format of the permalink to the posts, with a sane URL structure including the date and title of the post.

Instead of repeating ourselves in the frontmatter of each post we can DRY it up a bit by specifying defaults in the config.yml file:

      path: "" # an empty string here means all files in the project
      type: "posts" # previously `post` in Jekyll 2.2.
      layout: "post"
      excerpt_separator: "<!`--` more `--`>"
      comments: true
      permalink: "/blog/:year/:month/:day/:title"

The format YYmmDD is obviously locale specific - but one advantage with using this format is that your posts can be chronologically ordered even on the file system. The final URL for a post would look something like this:

With the configuration above applied content will only be shown on the home page up to where the content processor finds the <!--more --> marker:

Jekyll excerpt

Override the look and feel

We can create a main stylesheet in the assets directory in your source folder. main.scss will be the Sass file included after everything else so you can override any styles that you want. Since we've used the minima theme it's important that we import it at the top. Let's put in a separator between the posts:

@import "minima";

.post-list>li {
  border-bottom: 1px solid #b6b6b6;
  padding-bottom: 30px;
  margin:  10px 0px 30px 0px;

I'm not a big fan of the 800px fixed-width style - by adding the following CSS into assets/main.scss we can take up more real-estate on the screen:

.wrapper {
  max-width: none;
  margin-right: auto;
  margin-left: auto;
  padding-right: 10%;
  padding-left: 10%;

In order to override the markup provided by the default theme you can "eject", copy the files from the gem installation directory into your own source directory and modify them at will.

The command

bundle show minima

will show you the source directory to copy the files from.

Productionise it!

As with any software development it's important to get the production aspects out of the way. This can always be modified later on, but doing this very early in the process will eliminate a bunch of "whoops, didn't think about that" moments. Deploying to GitHub pages is a popular method of serving a blog made with Jekyll, but since I'll be deploying this on a private server we'll focus on building and running a docker container for this purpose. Let's create a simple NGINX server to host our application with this Dockerfile:

FROM nginx
COPY ./_site /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
COPY default.conf /etc/nginx/conf.d/default.conf

Simple enough. Jekyll generates the site static content under the _site directory which can we can copy into the default serving directory of NGINX. I've added a configuration file to add cache control headers for static content (images, js, css). HTML pages are still set for the browser to check back to server if any content has changed. We also enable gzip and set up NGINX to scan for the index.html file in directories to have nice file-less URLs:

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/;

events {
    worker_connections  1024;

http {
    server {
      location / {
        try_files $uri $uri/index.html $uri.html =404;

      location /assets {
      	expires 1M;
      	add_header Cache-Control "public, max-age=691200, s-maxage=691200";

      location /images {
      	expires 1M;
      	add_header Cache-Control "public, max-age=691200, s-maxage=691200";
      rewrite ^/(.*)/$ /$1 permanent;
      expires $expires;

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  65;
    gzip  on;

    include /etc/nginx/conf.d/*.conf;

At this point in time don't forget to update the header include template to add a cache buster for the stylesheet:

<link rel="stylesheet" href="{{ "/assets/main.css" | relative_url }}?{{site.time | date: '%s%N'}}">

The generated link to the stylesheet would look something like /assets/main.css?1519308247603964426. Simply, the time value at the point of generation would be appended to the stylesheet URL allowing us to effectively cache the asset forever on clients until it changes.

Working with drafts

In order to work on draft posts and not have them published, you need to create a 'drafts' in your source folder as per the documentation. When running jekyll serve , pass in the --draft parameter to show drafts. Depending on your needs it might also be useful to add the --future parameter in order to display posts that have a date in the future.


No good blog should be without paging. By default my installation came with the jekyll-paginate gem installed. I've found that the next iteration of the gem, jekyll-paginate-v2 works better and is simpler to implement.

In order to activate, add the jekyll-paginate-v2 to the plugins section of the config and your Gemfile. There are tons of configuration options for pagination, but here were the ones I found important for now:

  enabled: true
  collection: 'posts'
  per_page: 8
  permalink: '/page/:num/'

Where your posts are displayed you will need to iterate over the paginator.posts instead of posts:

for post in paginator.posts

If you are using the minima theme, it's time to copy over the home.html file from the layouts folder and modify it. We also shouldn't forget to add the pagination links at the bottom:

{% if paginator.total_pages > 1 %}
  {% if paginator.previous_page %}
  		<a href="{{ paginator.previous_page_path | prepend: site.baseurl }}">Newer</a>
  {% endif %}
  {% if paginator.next_page %}
      <a href="{{ paginator.next_page_path | prepend: site.baseurl }}">Older</a>
  {% endif %}
{% endif %}

This gives us an easy way to navigate between pages:

Jekyll pagination

Hmmm, functional, but not pretty. We'll improve on this in future posts.

Photo by on Unsplash