Reviving this website

Late last year I shut down the VPS hosting this site and let it disappear. I'm resurrecting it because I want a place to document my side-projects and I need another excuse to write more code by hand.

Motivation

It's simple really. I love using coding agents during my day job but I can't help but feel like I'm drifting further and further away from the code. I still spend most of my time reviewing and refining the code agents produce but I also feel the need to write code.

I discovered Handmade Hero towards the beginning of the year and it inspired me to think of side projects as a way to learn new concepts from scratch. One such project was re-building my website. I chose Go because I've always been intrigued by the language. After using it for a bit I can definitely say that while the syntax is ugly, it does grow on you and the productivity of the language and its stdlib is incredible. Almost everything that's needed to create and operate web services is built in directly in the language. And the things that aren't? Well, that's a good learning opportunity.

The code

I'm taking this opportunity to open source the code for this website. See the repo on GitHub. Hopefully it will be helpful to someone else.

With the idea of zero/minimal dependencies it constrains the code to be very simple and very clear on the external code it relies on. As such I've allowed myself use of the Go stdlib, their official /x/ modules and a single third-party dependency (for the website): an SQLite driver.

Other than the database driver, I trust the platform I build on. Posts are Go HTML templates, static files are served through Go's http.FileServerFS, the logger is Slog and so on. I had to implement my own rate limiter and system to manage post assets.

Posts are folders with a meta.json, post.html and optionally a folder assets. The website repository builds a secondary command to insert (or update) a row in the database from a directory.

Static assets are stored in the database through a content-addressed system. Each post contains a mapping from public filenames to their content-addressed rows in the database. Hash-addressed assets can be cached immutably while posts can use the filename mapping to update the assets they render on page.

Another feature the asset sub-system supports is serving files directly from zip archives. I wanted to do this in order to simplify my use of highlight.js. Could I have unzipped the archive in my static folder and be done with it? Yes. Would that have been as fun as reading bytes from SQLite and decompressing individual files from the archive? No. So here we are.

Deployment

This website is now hosted on a spare machine I have running in a closet in my home with a Cloudflare tunnel pointed at it. Go is pretty efficient and this website is really simple so it barely uses any CPU or memory (approx 8MB of RAM).

The build and deployment are all configured via Nix. The server exports a module for a systemd unit. Deploying this on a server just becomes a case of loading the Nix module and enabling the service. See the commit where this got added to my host.


age.secrets.website_env.file = ../secrets/website_env.age;
services.alexghr-me = {
  enable = true;
  environmentFile = config.age.secrets.website_env.path;
};

  

Updating the server code is a matter of updating the host's flake.lock and re-applying the config.

I'm hoping this iteration of my website will keep me motivated to document my work and inspire others to go back to writing by hand. I've got plans to publish my devlog while working on a Rust-based game where I do build everything from first principles (yay, Handmade Hero). I've also been working on a custom software factory (Pact) which I also plan to write about.

Back to blog