Jekyll is a static site generator, typically used for personal websites and blogs.
The link above contains documentation on getting started using the default theme.
Jekyll lets you write your site in Markdown which is output into HTML and CSS.
The HTML and CSS files are generated in
_site via the command
Once your website’s content has been built into
_site, a web server can serve
the files in
_site to web browsers.
First though, you probably want to run your site locally:
If you navigate in a browser to
localhost:4000, you should be able to view
But how to let anyone with an internet connection view your site?
You have to deploy your site to a (production) server.
There are different ways to do this. We will discuss some of them in the remainder of this post.
Conceptually, this is probably the simplest approach:
However, disadvantages are:
- lack of portability, e.g. changing server
- no version control (for the project itself and deployment configuration)
- single point of failure
- no automatic restart
- large duplication cost running staging and production environments
And it is difficult to:
- automate the deployment process
- collaborate with others on the project
- achieve load balancing, scaling
This list is far from exhaustive. Further, such problems will typically get worse developing more complex applications.
Containerisation allows you to isolate all the software and associated configuration required to run your application from the hardware hosting it and any other software running on the host.
It can be used to modularise parts of the deployment process.
It allows you to switch hardware or cloud technology more easily, e.g. from Amazon EC2 to Google Compute Engine.
The most popular container technology at the time of writing is Docker (see here for a simple use case of Docker).
How do we containerise our Jekyll website?
We bundle our web server and the files it serves together into a container.
This translates into the following
FROM nginx:alpine COPY ./_site /usr/share/nginx/html
which is turned into a Docker image via
The image can then be run on the host via
docker run, exposing our Jekyll site to the
Suppose you have several developers working on the same project which has a main
branch (typically called
After a developer has finished working on a bug fix or feature, they request for their copy of the code to be integrated into the main branch. The code is reviewed and modified if necessary, before becoming production-ready.
However, by this time the main branch has changed as other developers in the team have integrated their work.
As a result, the developer might have to resolve merge conflicts, refactor, or, in extreme cases, take a different approach altogether and start over.
Collectively, these problems are known as “integration hell”.
One outcome of “integration hell” is the developer spending more time changing their code at the point of integration than completing the original task.
In an ideal world, a developer “continously” integrates their code into the main branch as they make their changes.
In reality, continuous integration means taking measures to ensure the time taken for a developer to implement production-ready changes is short.
a) Giving developers more, but smaller, tasks, e.g. breaking up a large task into smaller ones
b) Automating code quality checks
As well as checking the code builds without errors, the build server runs automated tests and linters (code style checks, suspicious constructs, etc.), reporting back the results.
We are going to assume you have a Github account containing a repository for your Jekyll website and have completed the Github-CircleCI integration.
.circleci directory in the project root.
config.yml is parsed by CircleCI each time a commit is pushed to Github and contains the
configuration and instructions for our temporary build server.
The syntax is YAML which stands for “Yet Another Markup Language” or “YAML Ain’t Markup Language” depending on who you believe.
YAML is a superset of JSON. Here is a guide to YAML basics.
In CircleCI, workflows consist of jobs which consist of steps.
To keep things simple, we aren’t going to worry about automated tests or linting, only that Jekyll can build our website.
In CircleCI terms, this means we will have one workflow consisting of one job. The job
will output our site’s HTML and CSS files into
config.yml looks something like
version: 2 # CircleCI version jobs: build: # define our job `build` docker: - image: jekyll/jekyll:3.5.0 working_directory: /srv/jekyll steps: - checkout # checkout latest code in Github - run: command: ./build-jekyll.sh workflows: version: 2 build: # workflow is called `build` jobs: - build # workflow `build` consists of a single job, also called `build`
and in our project root,
#!/usr/bin/env bash set -xe mkdir site jekyll build
Now, the next time you push changes to your Jekyll repository in Github, this will trigger a build in CircleCI.
In CircleCI, you should be able to see this build and its progress as it executes the
We saw above how CircleCI runs automated checks to ensure software quality each time changes are pushed up to a repository. This was part of continuous integration.
If the checks fail, the developer responsible can make the necessary changes in order for the checks to pass.
If the checks pass, and the changes pass code review, they can be integrated into the main branch.
The next step is for these changes to be reflected in a new release of the software, i.e. for the changes to be propagated to a version of the software running in a production environment.
However, often the new release of the software will be first mocked in an environment identical (or very similar to) the production environment. This environment is typically called staging.
If this step is done manually, there will be a non-negligible delay between the time of integration into the main branch and the new release.
If it is done automatically, i.e. integration into the main branch triggers a sequence of actions leading to a new release of the software in staging, this is continuous delivery.
If it is done automatically and the new release is in production, this is continuous deployment.
In doing so, we will use containerisation in conjunction with Kubernetes, a container orchestration / management system.