A bit of a meta-post

Posted December 12, 2023 by Romulo Collopy ‐ 9 min read

Sharing with you how did I create this website

Disclaimer

This is not a how-to intended to be followed by the user that whishes to build their own website. Instead, this is a description of what made sense to me in the context of the tooling I am used on a daily basis. In another words, I will not go trough the many options that you have to create your website or blog. I am just describing how I created mine.

Things I picked to build it

  1. Docker
  2. Dokku
  3. Digital Ocean
  4. Zola
  5. Adidoks Theme

Docker

I think containers are a great technology to use as a developer. Given the number of tools involved in accomplish any simple task in this world, I prefer to have them running in their own isolated environments. I have tools that install directly on my machine, for those I largely take advantage of the asdf version manager to handle their multiple versions.

But when it comes to run applications, I prefer running then in isolation to prevent me from shipping something that worked in my machine because of some specificity of that and is not easily reproducible in the production environment. For that reason, my first requirement was being able to run this site locally in a container, and deploy using the same container image. 1

Dokku

As they describe themselves, Dokku is an open source PAAS(https://en.wikipedia.org/wiki/Platform_as_a_service) alternative to Heroku. It means that you can install it on your server and have a simple set of commands to provision you application based on configuration files stored in the application's repository itself.

As I wished to deploy containers and Dokku can build and deploy applications based in a Dockerfile located in the application repository, it sounded like a good alternative.

Ansible

Dokku's installation process is quite simple and is available for Debian and Ubuntu distributions of Linux. But even having just a small set of commands to run in my VPS, I prefer to have this process automated. That is when ansible comes to play: I use it to configure the VPS, add the desired credentials, deploy keys, and to keep my VPS up to date. I do not consider ansible the best tool to manage complex infrastructure in the cloud, but it served well my needs for this project.

Ansible-Dokku

Especially because there is an ansible's plugin that does exactly what I needed: it provides a simple interface trough manifests that makes it easy to manage a Dokku installation of your own. In my first pass, I've written ansible's inventories using the bash plugin to manage Dokku. But after finding this plugin, I deleted my custom code in favor or something maintened by people more familiar with both tools. There are some drawbacks on that, but I decided that I's rather contribute to this open source lib than trying to come up with a solution of my own.

Digital Ocean

Of course I could deploy my container on Google Cloud Run, Amazon's ECS or Azure Container Instances. Or, because I like Digital Ocean, I could even use their on container solution, The APP Platform.

They are all scalable solutions of big players and, honestly, using this services would not a big lock-in on those vendors. But the case is that I like having at least one VPS available for my experiments. And I want to be able to interact with the filesystem, to install and remove packages, to encrypt and store some back-ups etc. And I had already a VPS in Digital Ocean for that reason. And it would cost me nothing to run this project in this VPS. So, why bother paying more?

Digital Ocean can even launch a ready-to-use Dokku image as your VPS. So, I just created this new machine, moved my things to it using rsync and used the ansible-dokku setup to update the Operating System and Dokku's version.

Zola

Having decided that it would run in a container, Dokku and Digital Ocean, I could build it in any technology capable of running in a container. Could be a Wordpress website, A Django application, or even a Cobol Microframework. But I have been flirting with Rust for a while now and creating several pet-projects. And what a developer does when they like a language, is using every possible piece of software built in that language. Zola just sounded right.

Zola is the static site generator I've chosen to transform my Markdown-written words in HTML pages.

Adidoks Theme

Not much to say here. It's a beautiful theme and i just needed few tweaks to make it work as a plain "blog". I have followed the instructions from the link in this section's title and overwrited a couple of files. Assuming the mysite example in their docs, I just had to tell the content/_index.md file to use the theme's blog/section.html instead of the regular templates/index.html that would be expected.

# content/_index.md
+++
title = "All Systems Fail"
description = "A developer's blog page"
sort_by = "date"
paginate_by = 10
template = "adidoks/templates/blog/section.html"
+++

The blog content then needed to be added directly in the content/ folder, parallel to the _index.md file. I've also overrided the themes/adidoks/templates/macros/head.html file by my own macros/head by copying it.

$ cp {themes/adidoks/,}templates/macros/head.html

And adding some customiztion to remove twitter configuration, since I do not use the platform. The final layout seems like this:

.
├── Dockerfile
├── config.toml
├── content
   ├── authors
   │   ├── _index.md
   │   ├── _index.pt.md
   │   └── romulo-collopy.md
   ├── _index.md
   ├── _index.pt.md
   ├── my-post.pt.md
   └── my-post.md
├── templates
   └── macros
       └── head.html
└── themes
    └── adidoks
        ├── ...
        ├── templates
        └── theme.toml

If you are wondering about the .pt.md files, they generate the content in Portuguese using Zola's Multilingual sites support.

With everything setup, I created the Dockerfile as described under this option from their Deployment options.

FROM ghcr.io/getzola/zola:v0.17.1 as zola

COPY . /project
WORKDIR /project
RUN ["zola", "build"]

FROM ghcr.io/static-web-server/static-web-server:2
WORKDIR /
COPY --from=zola /project/public /public

Right now, I pushed the project to a git repository and deployed it using the Docker Builder from Dokku. Managed with ansible-dokku that I evoke using some Makefile shortcuts. I don't really like manually doing things because I'm sure I will forget some step. I am using this Makefileand ansible to deploy several different things in this VPS, and I intend to create a better structure to it and share here in the future.

But first, It's possible that I will search a self hosted option to add likes and comments to this website. After all, it's nice to know how are you liking those posts.

Deploy

The ansible infrastructure helps me setting up the Dokku install, creating the apps in Dokku, enabling certificates using dokku:letsencrypt and other task necessary to have a smoothly set application in Dokku.

But after the initial setup, the only action necessary to deploy is pushing the code to the repository in dokku. An application in Dokku set a repository up in the filesystem and listen for changes to the repository. If we want to serve the application as a subdomain, the easiest way to set it up, is creating the app with the full path of the application. In this example, it's www.paraplu.app. The git repository is created with the same name under the dokku username.

$ git remote add origin dokku dokku@my.domain:www.paraplu.app

Now deploying is as easy as:

$ git push dokku main

If you are familiar with Heroku, you will recognise this workflow.

Domains, subdomains and redirects

My domain is registered with Namecheap, and they have a limited DNS solution in the basic plan. I decided to create this project in the www subdomain, and assumed that I could jut redirect the traffic form the main domain http://paraplu.app to https://www.paraplu.app using the DNS configuration. And I can.

The problem is that NameCheap's solution won't respond if the client tries to access https://paraplu.app. Their solution does not respond in the port 443.

I could move the blog to the main domain. But I didn't want to change my planning because of an infrastructure limitation. If NameCheap cannot redirect, I can.

Using Dokku's Image Deployment, I've created an application from the nginx official image, and mounted a volume to override the images configuration in /etc/nginx/conf.d/default.conf.

Not so hard, you create a folder for storage, and mount in your application:

First I created the volume to be mounted:

$ dokku storage:ensure-directory my-proxy-configs

And added a default.conf file for nginx.

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

    #access_log  /var/log/nginx/host.access.log  main;

    server_name paraplu.app;

    return 301 https://www.paraplu.app$request_uri;
}`

Next, I created an application from the nginx image, mounted the configuration, and enabled https in the application.

$ dokku git:from-image my-proxy nginx:latest
$ dokku domains:add proxy paraplu.app
$ dokku domains:remove proxy proxy.dokku.me  # remove automatically created domain
$ dokku storage:mount my-proxy /var/lib/dokku/data/storage/my-proxy-configs:/etc/nginx/conf.d
$ dokku letsencrypt:enable my-prox

This last part I did not automate yet. One thing at a time, right?

Until then! Stay awesome.


1 I would like to try containerd as an alternative to docker, but I still have part of my tooling -- installation scripts, snippets -- that I also use in my company's laptop. And there no out of the box support for containerd on Mac sitll. Even though I should chek the Lima project.