Running Jekyll on Windows using Docker
What do I want to archieve?
My German blog runs on Jekyll. Jekyll takes a bunch of blog posts written in Markdown together with some layout and compiles them into a static web site. These files can then be hostet everywhere. I like editing the markdown files but it was a small pain to run Jekyll on Windows in the past. It became easier with the introduction of Windows 10 Subsystem for Linux (WSL for short). This worked okay but I found a much nicer way! Welcome Docker on the stage and the Docker image jekyll/jekyll. This image provides a running jekyll installation inside a container.
My target is to have two commands for my local command line which support my writing and my publishing needs:
- One command that will serve the local changes on my local maschine so I can see the changes live
- Another command to produce the static website content (html+css+images) and automatically uploads them
Installing Docker
Well, that is kind of easy. You open the docker page, click download, double click…
You want the Docker Community Edtion for Windows.
You may need to install the Hyper-V feature for Windows as part of the install. The installer is guiding you nicely here so just follow along. Then you have to enable file system access in the docker settings and provide credentials for this. Then you can already open up an elevated powershell and type
1 | docker run jekyll/jekyll |
This command will
- download the container
- start the container
- run its default command which seems to be just
jekyll
in this case - stops the container after the command exits
The error in the image is caused by jekyll not finding any actual blog yet.
Providing the right docker run
commands
All I need to do now is to provide the correct parameters to the docker run
command and everything should work. There are many parameters.
Mounting a volume
I want to have the actual source folder laying around in my PCs file system. So I have to mount them to the container to make them visible. The jekyll containter wants the blog data in /srv/jekyll
and I actually have them in C:\github\fabsenet\blog_de\
so I provide the mapping:
1 | docker run --volume=C:\github\fabsenet\blog_de\:/srv/jekyll jekyll/jekyll |
Mounting the current directory to a volume
I want to keep the command flexible. I want to be able to run it on different maschines for different blogs so I want to let it build the blog in the current directory instead. This can be done in powershell as well:
1 | cd C:\github\fabsenet\blog_de\ |
Mounting the cache volume to a persistent volume
The jekyll container does not have all the jekyll plugins my blog uses pre-installed so it will download them on every build again because the package cache is empty on every container start. Here I use the persistent volume:
1 | docker run --volume=${PWD}:/srv/jekyll --volume=jekyllbundlecache:/usr/local/bundle jekyll/jekyll |
jekyllbundlecache
is a simple name which I made up. Docker will create the volume on the first run and will reuse it next time. This avoids excessive package downloads and slowdown on consecutive run.
You can list existing (named) volumes with docker volume ls
:
Serving HTTP locally
I now want to serve the blog locally. If you want to change the command executed in the container, you simply name it at the end of the docker run command including its own parameters:
1 | docker run --volume=${PWD}:/srv/jekyll --volume=jekyllbundlecache:/usr/local/bundle jekyll/jekyll jekyll serve |
This will start jekyll serve
inside the container serving the site on port 4000. The problem is, it is not accessible from outside the container, so lets change that! You can bind a container port to a real port using the -p
option. In this case I am mapping the port 4000 to 4000:
1 | docker run --volume=${PWD}:/srv/jekyll --volume=jekyllbundlecache:/usr/local/bundle -p 4000:4000 jekyll/jekyll jekyll serve |
First part done! That is all I wanted for my first command.
Doing stuff interactively
I have no idea how to actually upload files in a Linux environment so I run the bash shell interactively in the container to figure that out. Please note the -it
flag to simulate a terminal in the container:
1 | docker run --volume=${PWD}:/srv/jekyll --volume=jekyllbundlecache:/usr/local/bundle -it jekyll/jekyll bash |
The -it
command emulates a terminal also in a way that it makes CTRL+c work as expected so this is also useful in serving jekyll locally!
To find an end to this blog post, I leave the code for the actually uploading out.
Doing more than one command in a container
If I change the command from jekyll serve
to jekyll build
it will build the site in the _site
folder. I prepared the upload command to upload this and I execute both of them using the bash -c
command:
1 | docker run --volume=${PWD}:/srv/jekyll --volume=jekyllbundlecache:/usr/local/bundle jekyll/jekyll bash -c "jekyll build && echo 'upload command here'" |
Second command done!
In summary: The commands are:
Serve locally on port 4000:
1 | docker run --name blog_de_serve -it --rm --volume=${PWD}:/srv/jekyll --volume=jekyllbundlecache:/usr/local/bundle -p 4000:4000 jekyll/jekyll jekyll serve |
Build+Publish:
1 | docker run --rm --volume=${PWD}:/srv/jekyll --volume=jekyllbundlecache:/usr/local/bundle jekyll/builder bash -c "jekyll build && echo 'upload command here'" |
The jekyll/builder image includes uploading tools like scp, lftp, …!
Thanks for reading!