Docker-Jekyll

docker jekyll websites

Backstory

This page will cover the (1) use and (2) creation of a Docker image for generating websites with Jekyll. Why would I do such a thing?

Well, the story is as follows. I maintain two websites, this one, and another one, that are generated with Jekyll. Every time I tried to update the other one (not amytabb.com), things were broken, I would have to fight with Ruby – something I know very little about – and a planned 5-minute update ended up consuming an inordinate amount of time. Since I update from more than one machine, my thinking was that I could freeze the Ruby version once and for all, and just run Jekyll from a container.

My Docker tutorial from 2018.

Getting a Docker image set up for amytabb.com was easy. Getting a Docker image set up for the other website never worked – and finally, I realized I would need to start with a template with fewer dependencies. Since I have very little on that other website (I am purposefully not linking to it …), this is not a big deal and will save me time and angst in the long run.

In the meantime, I had gotten a container working, so why not write a blog post? If you’ve been having a hard time getting Jekyll working, or there are incompatibilities with versions, this method should work no matter what OS you have. Technically, since Jekyll will be running from the container, even though I use an Ubuntu image, it should run.

Here goes!

Install Docker

If you don’t have Docker already, you’ll need to install it. Here’s the link to install for Linux, and I also endorse uninstalling old versions if you have them floating around.

Build image or pull from Docker hub

The image is currently at Docker hub, amytabb/jekyll-basic. To pull to your local disk,

docker pull amytabb/jekyll-basic

Alternatively, you can build the Docker image on your local disk yourself. The Dockerfile is in a Github repository, amy-tabb/docker-jekyll-basic

git clone https://github.com/amy-tabb/docker-jekyll-basic.git

cd into the directory, and then build:

sudo docker build -t jekyll-image .

Run Jekyll from container

Now it is time to run Jekyll from the container. We need a lot of stuff here. Assuming that jekyll-image is the image name (it would be amytabb/jekyll-basic:latest if you pulled it from Docker hub),

sudo docker run  --network host -v /home/atabb/git/amy-tabb.github.io:/write_directory -it jekyll-image jekyll serve

is one concrete example. I’m going to put in another example, because I myself use this post a lot.

Case where you are in the directory, and you are using the container from the Docker hub:

sudo docker run  --network host -v $PWD:/write_directory -it amytabb/jekyll-basic:latest jekyll serve

Substituting in the generics:

sudo docker run --network host -v /full/file/path/of/web/site:/write_directory -it jekyll-image jekyll-commands

Let’s start at the back – the jekyll commands.

For some websites, the commands are bundle exec jekyll serve. The template I am using is generated with jekyll serve, so I used that.

jekyll-image is the name of the docker image, again, you would use whatever name you selected when you built the image locally, or amytabb/jekyll-basic:latest if you pulled it from Docker hub.

-v /full/file/path/of/web/site:/write_directory means to bind the first path on the host machine, to the second path in the image (separated by a :). I set up the Dockerfile such that the working directory is /write_directory, so that will not change. -v indicates a bind mount (a type of mount in Docker).

--network host means to allow the container to access the host’s network. From the docs, this is only available in the Linux Docker version. But I really don’t know what the status is for other OS’s. I have complained about Docker’s docs before.

run: we’re running the image to get a container.

Details of the Dockerfile, extensions

FROM ubuntu:18.04

RUN apt-get update

RUN apt-get -y upgrade

RUN apt-get -y install ruby-full build-essential zlib1g-dev

RUN apt-get -y install ruby-bundler

RUN useradd jekylluser 

RUN mkdir /home/jekylluser

RUN mkdir /write_directory

RUN chown jekylluser /home/jekylluser

USER jekylluser

WORKDIR /home/jekylluser/

ENV HOME "/home/jekylluser"
ENV GEM_HOME "$HOME/gems"
ENV PATH "$HOME/gems/bin:$PATH"

RUN gem install github-pages

RUN gem install bundler

WORKDIR /write_directory/

I’ll explain each part. For more in-depth details about using Docker for research purposes, I have a tutorial that concerns using Docker to package code for those not on your OS – which in my case, is C++ with Linux.

  • FROM creates a layer from the ubuntu:18.04 image. This image is already created on Docker hub.
  • RUN ... runs instructions as we would from a bash shell, in this case:
    • updating the packages update,
    • and upgrading them to the most recent versions upgrade. Is this necessary? Maybe not for this particular project, but for code, it is.
    • install the relevant ruby libraries for Jekyll. From here, you will see many similarities to Linux installation for Jekyll.
    • useradd jekylluser: adds a new user, as in Docker we only have a root/superuser by default.
    • mkdir /home/jekylluser: we create a home directory for the new user.
    • mkdir /write_directory: we create a working directory for Docker, while we still have root access.
    • chown jekylluser /home/jekylluser: give the new user access to their home directories.
  • USER jekylluser: switch user to the new user.
  • WORKDIR /home/jekylluser/ switch to the new user’s home directory. I learned the hard way cd is not your friend in Docker: Docker: What’s the deal with WORKDIR?.
  • ENV: set environment variables, which is similar to the instructions relevant to ./bashrc from the Linux installation for Jekyll page.
  • RUN.. : I install the github-pages gem, and then install the bundler gem. You could add more gems if needed.
  • WORKDIR /write_directory/: set the working directory to /write_directory/. This is important as we’ll bind a directory on the host machine to this particular directory in the Docker image, so we need to make sure that the final WORKDIR is the second argument of -v /full/path/to/host/dir:/our/workdir/in/docker/image.

Final note: beware of relative and full paths in Docker. There are many ways to get things wrong. Docker: What’s the deal with WORKDIR? has a full explanation of these phenomenon, and to eliminate problems, I use full paths (starting with /). In short, if there’s a way to get it wrong, I’ve probably done it!

Extensions

Hopefully, you can see there’s plenty of ways you can alter this basic Dockerfile for custom cases, to install more gems or to extend functionality.

© Amy Tabb 2018 - 2023. All rights reserved. The contents of this site reflect my personal perspectives and not those of any other entity.