I recently stumbled upon the phrase of a digital garden. The idea of personal, online notebook/blog which is mainly orchestrated for the author itself rather than an actual audience. Combined with all the fuss about static site generators such as Hugo, Jekyll, and what not, I decided to create my own digital garden using a static site generator.

Setup

As with real gardens, digital gardens come in many shapes and forms. I guess the only real requirement is that it is online? But then again, real gardens are usually not available for public consumption. For my digital garden. I wanted something that was online, very easy to run, maintain and to write content for.

The Garden

My version of a digital garden is created using the static site generator Zola. I wanted something which was extremely easy to get up and running. If the process of doing scaffolding for some static site generator were too great I would eventually just abandon the project before the first content was even posted.

Getting started with Zola is pretty easy. The themes lets you skip the entire part about their templating engine. You can simply download the executable, do the zola init myblog, find and clone a theme and then start writing the markdown for your digital garden. Some of the themes require setting up taxonomies and categories which I really did not want to do at this point.

But I found and cloned the Solar Theme which looks pretty neat. I did however increase the font size so I did not have to squint.

The Property

You cannot have a garden without a property. In my case I already had a VPS running. I use liteserver.nl which is a hosting company located in The Netherlands. Why did I pick liteserver.nl opposed to DigitalOcean/OVH/Scaleway? I initially rented the server to create a Tor Relay, which you can read more about here, and most of the other Tor relays are VPSs from either DigitalOcean, OVH or any other of the large hosting companies. The Tor network needs to be diverse in order to not create single points of failure or allow for instance DigitalOcean way to much control over the Tor network.

So I found liteserver.nl instead and ordered a 5 EUR VPS, the HDD-1G plan to be exact. This provide you with 1 CPU core, 1 GB of RAM, 512 GB of HDD storage and 6 TB of bandwidth. Other hosting companies does not offer HDD storage at all and SSD storage is usually 0.10$ per GB and for my needs, HDD storage is sufficient.

The server is running Debian, as I have grown quite displeased with Ubuntu and how much snapd is being forced upon the user. You cannot have the newer Ubuntu without having snapd running all the time. I want my server to be a lean machine especially with 1 CPU core and 1 GB of RAM.

The Fence

I know, the real garden analogy is wearing a bit thin. Every server needs a bit of security in order to not get steamrolled by the myriad of bruteforcing/auto-pwning bots roaming around the web. I usually use a combination of only allowing SSH logins with key-pairs and ufw to close down any port I don't want to have exposed.

Unfortunately SSH logins with key-pairs only was not an option for this particular VPS as I also use it to sync data from my NAS which does not support logins with key-pairs. Instead I use ufw to only allow SSH logins from my home IP:

# Set default. Deny incoming and allow outgoing
ufw default deny incoming
ufw default allow outgoing
# SSH only from my own IP
ufw allow from ***IP*** to any port 22
# Allow all to view digital garden
ufw allow 80
ufw allow 443
# Allow all to tor relay
ufw allow 9001

nginx and SSL

I use nginx to serve the site. The configuration is pretty straightforward given that the site is static. I add some caching and allow nginx to compress the content.

server {
    server_name madsjoensen.dk www.madsjoensen.dk;
    root /var/www/madsjoensen.dk;

    gzip_static on;
    gzip on;

    location / {
        index index.html;
        expires 30d;
        add_header Cache-Control "public";
    }
}

I also use the EFF certbot to generate SSL certificates for my digital garden. Finally I do some redirect tricks such that "www.madsjoensen.dk" is always redirected to "madsjoensen.dk". It seems Zola does quite a lot of hardcoding in its static site generation and I ran into some glitches so to be safe all content is on the same URL. The final config is this:

server {
    if ($host = www.madsjoensen.dk) {
        return 301 https://madsjoensen.dk$request_uri;
    }


    if ($host = madsjoensen.dk) {
        return 301 https://$host$request_uri;
    }


    server_name madsjoensen.dk www.madsjoensen.dk;
    listen 80;
    return 404;
}

server {
    server_name www.madsjoensen.dk;

    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/madsjoensen.dk/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/madsjoensen.dk/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    return 301 https://madsjoensen.dk$request_uri;
}

server {
    server_name madsjoensen.dk;
    root /var/www/madsjoensen.dk;

    gzip_static on;
    gzip on;

    location / {
	index index.html;
	expires 30d;	
	add_header Cache-Control "public";
    }


    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/madsjoensen.dk/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/madsjoensen.dk/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

That is it. I simply do a zola build and sync the generated files to /var/www/madsjoensen.dk and the garden is up and running.

TL;DR

  • Zola with the solar theme as static site generator
  • Some CSS tweaks to increase font size
  • 5 EUR VPS from liteserver.nl
  • ufw
  • nginx
  • certbot
  • That is it

Update 2020-11-21

I bought an additional domain name mads-joensen.dk to add to my small collection of domain names. I wanted this domain name to redirect to my digital garden as well, so I updated the nginx configuration and get more SSL certificates using certbot. There is probably some better way to do this, but I rather want explicit and stupid than clever and convoluted. My configuration is now as follows:

# HTTPS redirects
server {
    server_name www.mads-joensen.dk mads-joensen.dk;

    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/mads-joensen.dk/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mads-joensen.dk/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    return 301 https://madsjoensen.dk$request_uri;



}

server {
    server_name www.madsjoensen.dk;

    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/madsjoensen.dk/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/madsjoensen.dk/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    return 301 https://madsjoensen.dk$request_uri;
}


# HTTP redirects
server {
    if ($host = www.mads-joensen.dk) {
        return 301 https://madsjoensen.dk$request_uri;
    }

    if ($host = mads-joensen.dk) {
        return 301 https://madsjoensen.dk$request_uri;
    }

    server_name www.mads-joensen.dk mads-joensen.dk;
    listen 80;
    return 404;
}


server {
    if ($host = www.madsjoensen.dk) {
        return 301 https://madsjoensen.dk$request_uri;
    }


    if ($host = madsjoensen.dk) {
        return 301 https://madsjoensen.dk$request_uri;
    }


    server_name madsjoensen.dk www.madsjoensen.dk;
    listen 80;
    return 404;
}


# Actual server config
server {
    server_name madsjoensen.dk;
    root /var/www/madsjoensen.dk;

    gzip_static on;
    gzip on;

    location / {
    index index.html;
    expires 30d;
    add_header Cache-Control "public";
    }


    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/madsjoensen.dk/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/madsjoensen.dk/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}