What I learned while things broke
A Change Of Post
The hawkeyed among you will have noticed that I have already changed the theme from the boring default theme to something a bit more cyberpunk. I’ve installed the rather fancy Hacker Theme as it not only looks better, but it also has better support for displaying inline code.
See?
Anywho, this site is something I’ve been meaning to start for a while. It is a place to put down thoughts that have been rattling around in my head and they may vary wildly, as all good thoughts do. Some of it will be about work, some will be more general throughs about the world in tech, for better or worse.
…Publishing a website on Fastmail - Part III
After many failed deploys and at least two existential crises, I went back to first principles: simplify.
No modules. No rclone. No trying to obscure passwords mid-script.
Just a GitHub Actions workflow that:
- Checks out the repo
- Installs Hugo
- Downloads the Ananke theme manually
- Builds the site
- Uses curl and MKCOL to recursively ensure folders exist
- Uploads files one-by-one to Fastmail via WebDAV
You’ll need an app-specific password from Fastmail to then add that to the secrets of your repo, but that is the only semi-complicated part of this process.
…To Do list
The blog’s alive — or at least limping around with just enough bones to stand up on its own. I can post entries, they get sorted into categories, and everything lands where it should. But there’s more to do. There’s always more to do.
Things I’d still like to build:
-Some kind of comments system (thinking something minimal, not trying to rebuild Reddit) that integrated with the Fediverse.
- ategory-level RSS feeds
- Referral tracking, mostly for curiosity
- Decent stats, my hosting provider has no stats system.
- Somewhere to stick longer pieces that don’t fit the “latest post” model
- Auto-link directory: pull links straight from posts and sort them somewhere useful
Tech-wise, this is all running in simple HTML/CSS since the whole stack is Hugo + GitHub Actions and then published over WebDAV. See previous posts for details.
…Publishing a website on Fastmail - Part II
The fantasy was simple: write a post, push to GitHub, and watch the blog rebuild itself like a phoenix from Markdown. The reality? CI pipelines are just slow-motion debugging sessions wrapped in YAML.
First Attempt: WebDAV or Bust
Fastmail supports WebDAV. Which is like FTP’s awkward cousin. My thinking was: write a GitHub Actions script to build the site, then push the generated files to Fastmail’s WebDAV endpoint. In theory, clean. In practice, cursed.
…Publishing a website on Fastmail - Part I
I can safely say that I haven’t been on the internet for about four years now ever since I decided to leave social media permanently by deleting by Facebook, Twitter, and YouTube profiles. 14 years of my life gone in an instant, and losing touch with lots of people as a result.
Best decision ever.
Since then, Facebook has slipped further into the abyss of rage-baiting and trauma monetisation; and Twitter… you know the rest.
…O HAI!
Welcome to my blog powered by Hugo, and hosted on Fastmail.
This is my first post, so consider this a Hello World! post without calling it that because thats far too generic and where is the fun in that?
Also, everything is (orignally) written in Markdown because that is the only file format you need to write in.
More groundbreaking insights coming soon.
…First Post
Sometimes you need to stop fucking around and just start doing, thats what I am now doing.
In short, I am going to use a Static Site Generator (probably Hugo) to create a website, and then host that website on my Fastmail account.
Since Fastmail supports WebDAV I have an idea of using GitHub actions to build the site, and the publish it. All by using Git.
This has the advantage of being able to make changes to my site virtually anywhere including my favourite IDE which is VSCode. While I am not a fan of Windows, I still use some of Microsoft’s more functional products.
…---
title: "The *arr Stack, part 3: cloud-init bootstrap"
description: "Bootstrapping the VM without SSHing in like a caveman"
date: 2026-02-04T11:00:00
tldr: ""
draft: false
tags:
- Article
---
## The Goal
In part 2 we created a VM that is basically an empty rental flat: it exists, it has a key, but there’s no furniture and the lights are flickering.
This part is about **cloud-init**. I want Terraform to:
- deploy the VM
- feed it a bootstrap config
- and then the VM comes up with:
- a non-root user
- Docker installed and started
- basic folders created for the stack
All without me SSHing in and doing a little manual dance each time.
## What cloud-init is (and why I’m using it)
Cloud-init is a standard tool for initializing cloud instances. Linode (Akamai Connected Cloud) has a **Metadata service** that cloud-init reads on first boot, including any **user data** you attach at provisioning time. :contentReference[oaicite:0]{index=0}
So: we give the instance a small YAML file (cloud-config), the instance boots, cloud-init runs once, and we get a predictable baseline.
Also important: user data needs to be **base64 encoded** when it’s submitted. Terraform can do that for us. :contentReference[oaicite:1]{index=1}
## Repo changes
We’re going to add a cloud-init template so we can inject variables like username and SSH key.
```text
repo/
bootstrap/
cloud-init/
cloud-config.yaml.tftpl
infra/
envs/
dev/
main.tf
variables.tf
outputs.tf
versions.tf
terraform.tfvars
Step 1: Create the cloud-init template
Create: bootstrap/cloud-init/cloud-config.yaml.tftpl
---
title: "The *arr Stack, part 4: Perimeter security"
description: "Defence in depth, and how not to lock yourself out lel"
date: 2026-02-04T13:00:00
tldr: ""
draft: false
tags:
- Article
---
## The Goal
Right now we have:
- a VM
- a user
- Docker
- folders
Which is nice, but also means the VM is one typo away from being “publicly accessible surprise box”.
This part is about perimeter security using:
- **Linode Cloud Firewall** (network edge, outside the VM)
- **UFW** inside the VM (host firewall, inside the VM)
I’m doing both because I’m paranoid and I like sleeping.
## The shape of the rules
I’m keeping it simple:
Inbound:
- allow SSH **only from my IP**
- (optional) allow WireGuard UDP port for later VPN stuff
- everything else: drop
Outbound:
- allow (updates, pulling containers, DNS, etc)
If later I decide I actually want a public reverse proxy, I can add 80/443. But for now my stated goal is: **internal HTTPS and not exposed to the internet**.
## Step 0: Don’t brick your access
If you do this wrong you will lock yourself out.
Two escape hatches exist:
- Linode console access (Web console / Lish)
- Temporarily loosening the Cloud Firewall rule
Still: avoid the drama if possible.
## Terraform: Cloud Firewall
### Step 1: Add variables
Edit `infra/envs/dev/variables.tf` and add:
```hcl
variable "admin_cidr" {
type = string
description = "Your public IP in CIDR form, e.g. 203.0.113.10/32"
}
variable "vpn_udp_port" {
type = number
description = "UDP port to allow for VPN later (WireGuard default 51820)"
default = 51820
}
variable "enable_vpn_port" {
type = bool
description = "Whether to open the VPN UDP port in the Cloud Firewall"
default = true
}
Step 2: Set your IP in tfvars
Edit infra/envs/dev/terraform.tfvars: