Running Jekyll with Docker

  • henok_mikre

    Henok Mikre

We've been using Jekyll for all kinds of projects over the last five years. It's been good to us. Setting up and maintaining local installations, however, has not been the most pleasant experience. We would have remember to have the right permissions for global and user gems, execute commands by prepending bundle exec when appropriate, and remember to update our PATH whenever we upgrade Ruby, etc. There was also the occasional system upgrade like when macOS implemented "System Integrity Protection," which required extra plumbing. It was therefore a great opportunity to improve this experience when I recently upgraded my machine.

Old Method: Local Installation

In the old days, I would install Homebrew and use that to install Ruby, Bundler, and Jekyll. It seems easy enough. But those who have tried it know that one wrong command could mean having to dig through various paths to fix permissions, etc. Here are the steps I used to follow:

# Install brew.
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

# Install command line tools.
xcode-select --install

# Update path to use brew install.
export PATH=/usr/local/bin:$PATH

# Modify path for GUI apps.
launchctl setenv PATH “/usr/local/bin:$PATH”

# Install a sandboxed ruby with brew.
brew install ruby

# Tell Rubygems not install docs
echo "gem: --no-ri --no-rdoc" > ~/.gemrc

# Add user gem dir to your path.
echo 'export PATH="~/.gem/ruby/2.6.0/bin:$PATH"' > ~/.bash_profile

# Now install bundler and jekyll for current user.
gem install --user-install bundler jekyll

# Install bundles for existing project (ensure vendor is excluded in _config.yml).
cd project/site/
bundle install --path vendor/bundle

# Verify version (prepending 'bundle exec' allow us to use project-specific version)
bundle exec jekyll --version

New Method: Docker

My first step was to create a Dockerfile inside a Jekyll repo and build an image from it:

Dockerfile:

FROM jekyll/jekyll:3.8

ADD . /srv/jekyll

RUN jekyll build

EXPOSE 4000

Building the image:

docker build -t blen_jekyll .

Running the container

docker run -d -p 4000:4000 -p 35729:35729 -it --volume="$PWD:/srv/jekyll" --name blen blen_jekyll /bin/bash

That seemed to work just fine. So the next step was to create a docker compose file and optimize our workflow by adding --livereload and --drafts options to the serve command. Plus, we don't want to have to remember the long command for docker run.

Here is our docker-compose.yml:

jekyll:
  image: jekyll/jekyll:3.8
  command: jekyll serve --livereload --drafts
  ports:
    - 4000:4000
    - 35729:35729
  volumes:
    - .:/srv/jekyll

Now all we have to do is run docker-compose up and point the browser to http://0.0.0.0:4000.

Trade-Offs

One difference I noticed is that the regeneration and auto-reload processes take longer with Docker than when using a local installation of Jekyll. That may have something do with performance issues of volume mounts. And there may not be much we can do about that.

Resources