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:
No fancy admin panels. I want to write markdown and deliver it as is.
Just a number of visitors per article per day is enough for me.
Complete control over my data.
I would like to have comments on my website in the future.
A popular blogging platform such as wordpress.com or blogspot.com.
The choice does not provide as much flexibility as I wanted, it has privacy issues, and I don’t have any control over my data at the end of the day.
The entire platform can be blocked in specific countries sometimes: list of contries blocking blogspot.com on Wikipedia.
Almost a flawless hit.
But it’s way too static, which means I’m not going to be able to have comments on my page unless I use an external solution.
The shipping of something like Disqus sacrifices privacy and leads to no data control.
Things like Disqus also have a price (or they show ads).
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.
Manually, on the remote host, you would end up with commands like:
- Pull the Docker image:
$ docker pull kovetskiy/my-blog
- Stop and delete existing Docker container:
$ docker rm -f my-blog
- Start a new Docker container:
$ docker run --detach --restart=always --name my-blog -p 80:80 kovetskiy/my-blog
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
- hosts: my-server tasks: - name: Pull image docker_image: name: kovetskiy/my-blog - name: Start container docker_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
To tell Ansible where the list of server is, put the following content into
[defaults] inventory = hosts
Add your server into the
hosts file and replace
126.96.36.199 with the host’s IP address:
The deployment will be done with one small command from now on:
$ ansible-playbook my-blog.yaml
How I self-host my blog:— Egor Kovetskiy (@reconquestio) November 5, 2020
* Static site generated with Hugo
* Packaged with Docker: docker build
* Linux host/droplet on Linode/DigitalOcean
* Ansible for the deployment of Docker containers
Which topic to expand first?
I also self-host an open source comments system without sacrificing any privacy. 👇