Run Nikola Blog in Docker

A lot of you might have a blog or a personal website created by static generator. Thanks to their simple requirements (just a webserver, really), they are an ideal starting point for your dockerization journey. In this post, I will explain how to run a Nikola website in a container. Nikola powers this website and is my static generator of choice. But the steps should be fairly similar for other generators out there.


The dockerfile I am using looks like this:

FROM python:latest AS builder

# Copy the whole repository into Docker container
COPY . . 

# Build the blog
RUN pip install nikola \
    && run nikola build

FROM nginx:alpine

# Copy output to the default nginx directory
COPY --from=builder output /usr/share/nginx/html

# Copy nginx host configuration
COPY nginx/default.conf /etc/nginx/conf.d/

As you can see, to make the final image as small as possible, I use a two stage build. In first image, builder, I install nikola with its dependencies and generate the website. Since Nikola is written in Python, I have conveniently chosen the offical image.

For the final image, I use the Alpine flavor of Nginx for its size. You might want to opt for Caddy, Apache2 or another webserver of your choice. The steps to make the image are very simple. Firstly, copy the output directory from the builder image. Secondly, add a custom Nginx configuration.

Nginx configuration

The configuration I use for Nginx is fairly basic, just adding compression and browser-side caching of static assets. Notably, it does not have any HTTPS configuration, because I normally use a reverse proxy for that purpose.

server {
    listen       80 default_server;
    listen  [::]:80 default_server;

    root   /usr/share/nginx/html;
    index  index.html index.htm;

    # assets, media
    location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
            expires 30d;
            access_log off;

    # svg, fonts
    location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff|woff2)$ {
            add_header Access-Control-Allow-Origin "*";
            expires 30d;
            access_log off;

    # compression settings
    gzip on;
    gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_vary on;
    gzip_buffers 16 8k;

Starting the container

Now you are ready to build the image using docker build . -t blog:latest. This assumes you run it from the directory containing the dockerfile and call your image blog. After the build completes, you can start the container by running:

docker run --restart=unless-stopped \
            -p 8080:80 \
            --name=blog \

This will start the container, listening on port 8080. Alternatively, you can use docker-compose.yaml:

version: '3'
    container_name: blog
    image: blog:latest
      - "8080:80"
    restart: unless-stopped

And start the container by running docker-compose up -d.


If you are just starting with Docker, this hopefully gave you an idea how to easily convert your static website to a container. The next step, is to deploy it automatically.