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.