Setting up this blog

October 18, 2026 heart

I just finished setting up this blog. Or did I? As I am writing this, I am starting to realise that this project—this journey—that is my new home on the internet, may never be finished. It may not even have a start. This is not my first blog. But I have never had another one exactly like it. It is unique in that sense. It is my little garden of thoughts and creations.

It is built with Zola, served by Caddy on my own little VPS on a machine somewhere in France that I pay €6.25 for each month at Gandi. That price does not include the domain registration, which I already paid for last year.

I started making websites in 2002, and I have to say that I am impressed how easy it has become since. Of course, I have also learned a lot since then. But when this (/etc/caddy/Caddyfile):

sarisakse.com {
    root * /path/to/sarisakse.com
    encode gzip zstd
    file_server
}

is everything you need to configure to set up SSL/HTTPS for your website and serve it statically, I do feel like I wasted a lot of time learning how to configure Apache or nginx, and setting up self-signed certificates.

Of course, setting up caddy itself is as easy as:

$ sudo pacman -S caddy
$ sudo systemctl enable --now caddy

Zola was the thing that had the steepest learning curve. Its documentation was not obvious to me, but after some experimentation and help from Lumo, I did eventually figure it out. And it’s actually quite pleasant to work with!

I also had a website on this domain before. It was just my gallery, but it worked pretty well. I had written a Python script that statically built the HTML for me based on some photos in a folder. It would use ImageMagick to convert the original files to smaller JPEG thumbnails, and the CSS was served as-is. When setting up this blog, I still wanted things to work that way. But I didn’t want to continue maintaining my old Python script. Luckily, Zola did it all.

This is where I have to admit that Lumo helped a lot. Zola uses a template engine called Tera. The syntax is mostly self-explanatory, but I didn’t really feel like reading the full manual just to get started. So after supplying Lumo with my Python script, I learned that Tera could not only generate the gallery, but it could also replace ImageMagick entirely, and do cached resized images automatically using the resize_image function. All I had to do was to put my images in a content/gallery/ folder, with an index.md that contained:

+++
title = "Gallery"
template = "gallery.html"
sort_by = "date"
+++

and then create a templates/gallery.html with my Python code translated to Tera.

The thing that actually took me the longest to understand, is Zola’s concept of an “index”, “page” and “section”. Zola, by default, will apply one of three templates to your Markdown pages automatically. For your root (content/_index.md), it uses templates/index.html. Mine simply reads:

{% extends "section.html" %}

as I want to use the same template for both the root index and my section indices. The templates/section.html is applied to sub-directories inside content. For example, I have a directory called content/blog for this blog, which contains content/blog/_index.md. That file is automatically templated by templates/section.html. And it reads:

{% extends "base.html" %}

{% block content %}
<div id="page">
<h1>{{ section.title }}</h1>
{{ section.content | safe }}
</div>
{% endblock content %}

Here, templates/base.html is the template for the full page, including the raw HTML for the header, navigation and footer. I could probably do something smarter with those too, but I actually like to keep that as plain HTML.

For the individual pages, like a blog post or the about me page (content/about.md), it uses templates/page.html. That one, I have defined as:

{% extends "base.html" %}

{% block content %}
<div id="page" class="post">
<h1>{{ page.title }}</h1>
<date>
  {{ page.date | date(format="%B %-d, %Y") }}
  <img src="/icon.svg" alt="heart" class="heart">
</date>
{{ page.content | safe }}
</div>
{% endblock content %}

Note that for the page template, you have to refer to page.title, not section.title. The actual content is then written as pure markdown files like this one (at content/blog/2026-10-17-setting-up.md):

+++
title = "Setting up this blog"
date = 2026-10-18
+++

I just finished setting up this blog.
Or did I?
...

There are a few extra nice things to know. If you place a:

<!-- more -->

in your markdown, this indicates to the RSS feed generator where to “cut it”. Content after that marker is hidden in the feed. Which is nice, if you don’t want your feed to contain your entire blog post. In addition, I have the following in my zola.toml:

base_url = "https://sarisakse.com"

generate_feeds = true
hard_link_static = true
feed_filenames = ["atom.xml", "rss.xml"]

[markdown]
render_emoji = true
smart_punctuation = true
external_links_target_blank = true

[markdown.highlighting]
theme = "rose-pine"

Finally, I use this little script (deploy.sh) to publish the website to my VPS:

#!/usr/bin/env bash
set -euo pipefail

zola build
rsync -avz --delete \
  --chown=caddy:caddy \
  public/ user@host:/path/to/sarisakse.com/

echo "Deployed: https://sarisakse.com/"

And that’s it!

I hope this inspired you to set up your own little personal website. If not, well, that’s also fine.