How I self-host my blog with Docker & Ansible

I have always wanted to build a blog, but for a long time, I have been postponing it.

Well, I guess I thought it was difficult to build a blog. Turns out, even if you self-host it, it’s not that hard.

I decided to finally create it one day, and I had a few criteria:

Considered options


Self-hosting has never been so easy, the only hard requirement is to have a Linux server costing $5 a month for 25GB SSD, 1GB RAM and 1vCPU.

I have selected Hugo for site generation because it is very popular, has a large supportive community, uses markdown, and has a lot of free themes.


Packaging and distribution is done using Docker. This is how tiny my Dockerfile looks:

FROM alpine as build
RUN apk add --update --no-cache hugo
WORKDIR /srv/http/
COPY . /srv/http/
RUN hugo -e production

FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /srv/http/public /srv/http/
WORKDIR /srv/http/

This ends up as a Docker container with Nginx and static HTML and CSS files.. The docker image is approximately 52MB in size.

Just two commands and the blog is ready for deployment:

$ docker build -t kovetskiy/my-blog .
$ docker push kovetskiy/my-blog


A manual way of deployment and an automatic way are available.

Manual way

Manually, on the remote host, you would end up with commands like:

Automation with Ansible

A playbook is a list of tasks that should be executed to accomplish the desired status. In my case, it is pulling of the Docker image and starting a Docker container.

The playbook is saved as a file called my-blog.yaml:

- hosts: my-server
    - name: Pull image
        name: kovetskiy/my-blog

    - name: Start container
        name: my-blog
        detach: true
        image: kovetskiy/my-blog
        restart_policy: always
        state: started

Ansible needs a bit of project-wide configuration, it just has to understand how to connect to this my-server server.

To tell Ansible where the list of server is, put the following content into ansible.cfg:

inventory = hosts

Add your server into the hosts file and replace with the host’s IP address:


The deployment will be done with one small command from now on:

$ ansible-playbook my-blog.yaml


I also self-host an open source comments system without sacrificing any privacy. 👇