Simple Websites with Jekyll and Docker

8 minute read Updated

How to host your own simple Jekyll websites on DigitalOcean using Docker.
Table of Contents

Looking to create a simple website but don’t want to pay through the nose for hosting? Get started today for free with Jekyll and Docker.

Primary Objective: Create and host simple websites with Jekyll and Docker using Mark Otto’s beautiful and minimalistic Lanyon theme.
Secondary Objective: Earn some street cred by learning how to use Jekyll, Docker and Passenger; and how to deploy your website from the command line.

Create Docker image

Alright, now onto the fun stuff! In this section we’re going to clone a seed for our Jekyll site and use it to create a Docker image we can run the same site both locally and on a server.

Never used Docker? No worries! Just head over to the Docker Jumpstart by Andrew Odewahn and start learning right away (recommended to continue).

Install Docker

Docker is designed to run on a variety of platforms including macOS, Windows and several varieties of Linux. To get Docker head to Install Docker on the docker docs website. Please install Docker before proceeding.

Clone Jekyll seed

In order to build a Docker image to run our Jekyll site we need an actual site to start with. Rather than creating one from scratch we’re going to use an open-source seed called Lanyon by Mark Otto. Go ahead and clone it now:

git clone https://github.com/poole/lanyon.git lanyon && cd $_
Tip: Looking for something more intersting or complex than Lanyon? Check out some of the wonderful themes created by the Jekyll god Michael Rose.

Configure Docker host container

Next we’ll configure a Docker container to host a Docker image we can use to run our site on DigitalOcean. Start by creating a Dockerfile, Gemfile and nginx.conf file in the root folder of the cloned Jekyll seed, modeling from the following:

# DOCKER-VERSION 1.6.0, build 4749651

# habd.as Dockerfile
# Runs Jekyll under Nginx with Passenger

FROM phusion/passenger-ruby21:0.9.15
MAINTAINER Josh Habdas "jhabdas@protonmail.com"

# Set environment variables
ENV HOME /home/deployer

# Use baseimage-docker's init process
CMD ["/sbin/my_init"]

# Expose Nginx HTTP service
EXPOSE 80

# Start Nginx / Passenger
RUN rm -f /etc/service/nginx/down

# Remove the default site
RUN rm /etc/nginx/sites-enabled/default

# Add the Nginx site and config
COPY nginx.conf /etc/nginx/sites-enabled/webapp.conf

# Install bundle of gems
WORKDIR /tmp
COPY Gemfile /tmp/
COPY Gemfile.lock /tmp/
RUN bundle install

# Add the Passenger app
COPY . /home/app/webapp
RUN chown -R app:app /home/app/webapp

# Build the app with Jekyll
WORKDIR /home/app/webapp
RUN jekyll build

# Clean up APT when done
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

What this will do is build a container image based off a container called passenger-docker, exposes traffic on 80 for port-forwarding, copies the site into the container, builds the site using Jekyll, configures Nginx to use it and then serves the whole thing up using Passenger.

Install Ruby dependencies

The dockerfile wants a file you don’t have yet called Gemfile.lock. To create it use Bundler from the same directory as the Gemfile to create it:

bundle install

With the lock file is created you’re ready to build the container image.

Build and run container image locally

Build the container image with the following command:

docker build --rm .

The above builds the image and removes intermediate containers after a successful build. Be patient while Docker pulls dependent layers during this process. Once completed, however, dependent layers will be cached during subsequent builds, making them faster.

After the build completes, get a listing of images:

docker images

Look at the top of the resulting list and copy the ID of the most recent image. Then run it in the background using the -d flag, forwarding port 80 on the container to port 80 to the host:

docker run -d -p 80:80 b64bea84255a

Verify the container is running with:

docker ps

In this list look for an active container, meaning the image is up-and-running on port 80.

Connect to Jekyll site

With the container image running confirm it’s serving your Jekyll site:

curl $(boot2docker ip) # or localhost on linux
Note: See the Docker Docs website for help with the latest commands.

You can even connect to the above IP using a web browser. If everything’s working correctly you should see something like this:

browser screenshot

That wasn’t so tough now, was it? Now that we’ve got a simple Jekyll website running under Docker let’s go ahead and deploy it to DigitalOcean.

Configure Server for Docker deployments

With the Docker image created we’re ready to host our site. For simplicity we’ll use DigitalOcean and take advantage of their 1-click Docker insta`llation.

Profit: If you don’t already have an account with DigitalOcean you may use my referral code and receive $10 in free credit toward usage fees. Steps may also be adapted for Vultr, Linode and Lightsail.

Create Docker Droplet

Login to DigitalOcean Control Panel and choose Create Droplet. Complete the subsequent form, selecting the $5/mo. size option and, in the Select Image section, click Applications and choose Docker as shown here:

docker droplet screenshot

Then Add SSH Key and create your droplet. Provisioning should finish in a matter of seconds. Once finished, create a new user account on the droplet.

Create new user

Once your droplet is provisioned create a new user so your not always executing commands using root. And while this is not strictly necessary, it’s beneficial for security and a good habit to get into anytime you create a new droplet.

The new user will be created remotely over SSH using a terminal emulator such as iTerm 2 or cmder but just about any terminal emulation software should do.

SSH into your droplet by IP as the root user:

ssh root@45.55.234.56

The first time you connect you should receive a prompt regarding host authenticity. When prompted type yes then press Return to continue connecting once you’ve verified the host is indeed yours.

Note: If you configured SSH keys when creating your droplet, you should now be connected to the droplet and logged in as root user. If not, you will need to enter the root password to continue connecting.

Once connected create a new user called deployer and give them super user privileges as described in How To Add and Delete Users on an Ubuntu 14.04 VPS.

Type exit followed by Return to end the remote connection. Next we’re going to add an authorized key to deployer for easy access to the remote.

Add SSH key for deployer

Adding an SSH key for deployer enables you to connect to the droplet without having to enter a password every time — both a convenience and security measure.

With deployer created go ahead and go ahead and add your SSH key as an authorized key with the following one-liner:

cat ~/.ssh/id_rsa.pub | ssh deployer@HOST "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

Where HOST is the IP of the droplet.

You will be prompted for the password the first go-around so go ahead and enter it. After that you will no longer be required to enter the deployer password while connecting from this machine.

Run the site on DigitalOcean

To run our Jekyll site on our droplet we’ll first create an archive of the site source. Then we’ll upload the site source to DigitalOcean, build and run it.

Upload site

Now that we’ve got a functional container image let’s go ahead and use it on DigitalOcean droplet with Docker pre-installed.

First commit any changes you’ve made and create a gzipped tarball of the source:

git add -A && git commit -m "All the things" && \
git archive -o app.tar.gz --prefix=app/ master
Note: This is a good time to push your changes to your own git repo.

Next use Secure copy to transfer the site’s source files from the current host to the droplet and rebuild the container image there:

scp app.tar.gz deployer@HOST:

Where HOST is the IP of the droplet created earlier.

This will copy the site archive to the server where it can be built and run.

Build and run container image remotely

With the app source uploaded to the server we’re ready to build and run the container image remotely. To do this first login to the remote server:

ssh deployer@HOST

Where HOST is the IP of the droplet.

Once logged in to the remote, unarchive and build the site, tagging it webapp to give it a more friendly name:

tar zxvf app.tar.gz && docker build -t webapp app/

Once the container image is built, run it on port 80 for both host and container:

docker run --rm -p 80:80 webapp
Note: Run the above with the -d flag once you’ve confirmed the site is up to keep it running.

Confirm Docker web server is up

Now that the site’s up and running you can test it by browsing to http://HOST/ where HOST is the IP of your Droplet. If everything worked you should be staring at the same Jekyll site you saw on your local machine earlier.

Stopping the site

When we’re ready to bring your site down you can do so doing the following on the remote server:

docker stop $(docker ps -lq)

Wrapping up

In this tutorial we learned how to create simple websites with Jekyll and Docker. Additionally we learned a simple workflow to deploy them to DigitalOcean via the command line, using Passenger to serve them with minimal required configuration.

Extra credit

Here are some ideas for things try next once you’re happy with the look-and-feel of your new Jekyll site:

  • Host your Docker container on DockerHub. They’ll even automatically build images for you when you commit to GitHub or BitBucket.
  • Install Octopress 3 and use it to simplify the process of managing pages and posts.
  • Automate the deployment process using drone or Octopress.
  • Learn about and set-up zero-downtime deployments using a proxy and tools such as flocker or Ansible.
  • Sign-up for a free CloudFlare account and use the CDN to boost site performance.
  • Purchase a vanity domain name and use it to route DNS traffic to CloudFlare or directly to the app running on the Droplet.