Hello internet, it's been a while. I've been working on something for a while, and today's the day I get to finally release it!
Yes, I redid my website - again! But, depending on how often you talk to me, I redid my website finally. This update has been a long time in the making. Since early 2021 I've known how I wanted the new site to work, how it should look, and even had a list of features - but I never quite had the motivation to actually start. Eventually, I got tired with the limitations of my current website (more on that later) and I stopped having excuses not to do it, and so here we are.
If you're new here, welcome! This is my website. It's part portfolio, part blog, part project list, part project. It's my place on the internet to post the things I've done, the things I'm thinking about, and help teach others. As websites go, it's nothing especially complex, but as simple blogs go, it's got some interesting extra features an easter eggs. Like the music section, or that stylish "scroll to top" button in the footer.
Previously, my website was based on Hugo, a static site generator. I wrote that site several years ago after getting annoyed with having to bodge around Pelican to make it do what I wanted. Hugo has served me well for that time, adding a number of useful features like SCSS, JS pipelines, Sub-resource Integrity etc.
My biggest annoyance with Hugo has to be the template language - it's horrible. Go's template language is designed with the idea that most of your logic should live in Go land, and just use the template engine to template things. That works absolutely fine in most cases, but if all you have is the templates, then they're quite limiting.
And that's before I start on Go as a language...
Hugo is a static site generator, which means its main purpose is to convert markdown files into fully-fledged HTML pages. Whilst it has some ways of pulling in API data, it's just not designed for it like tools such as NextJS are. I had to write my own service to pull data from Spotify's API to build music pages, and used GitHub's public API to natively embed Gists. I've been wanting to try to make the content more dynamic as time went on, and having to do terrible things to bodge around it wasn't making life easy.
In addition to consuming APIs, I also wanted to expose a few. Building little REST APIs is quite fun, and can make doing certain things a lot easier. I've spoken before about how I wrote my post link grabber, and I want to do more tiny things like that in future. There's also a Discord bot in the selfhosted.show guild which currently reuses the inefficient search index I wrote, but now has its own specific search API (which it's not using quite yet!
Not only all of this, but I just wanted something new - My website was starting to stagnate. Much of the CSS was painful to maintain, after being rewritten a few times and hacking around an number of Bootstrap's custom elements (no way I was upgrading to Bootstrap 5 anytime soon). The site was always in a dark mode, which I bolted on after the fact, which required yet more CSS hacks.
Professionally, I'm a "Senior Systems Engineer" (think Sysadmin, SRE, Solutions Architect, Security Engineer and IT admin all rolled into 1), which means I know my way around managing applications on servers. A static site is kinda boring: Take a server like nginx, point it at a directory, and you're all set (well, I use
traefik-pages, but that's kinda the same thing). I wanted something a little more interesting, something I could flex the skills I use at work on, and something to practice said skills on outside of a client-paying production environment.
Unsplash was the final nail in the coffin for my old setup.
The majority of the fancy header images you see at the top of my posts come from Unsplash, a fantastic service which provides a huge library of images free to use (provided attribution). As API's go, Unsplash's is quite nice - pass an API key in the query string, give it an image ID and you'll get back some metadata including multiple sizes of image. Better still, Unsplash actually require you to hot-link the images as opposed to downloading them and serving yourself - which saves on my network bandwidth and serves them using a nice fast (ish) CDN.
However, it's not all perfect: rate limits. Unsplash only allow 50 requests per hour. Each image requires 1 API call (thanks Hugo request caching), meaning after 50 images, it became basically impossible to build my website. There was a period of time where it became unreliable to build, but after a while it was consistently failing. My last successful website deploy was back in June! When reaching out to Unsplash to gain access to their "production" tier, which increases the rate limit, they appeared not to be able to comprehend my use case, requesting that I send them an API request every time "one of my users uses an image" - which makes no sense on a static site.
And so, there I was, left with a fully functioning website which wouldn't build. I could absolutely have worked around it, either by persisting Hugo's request cache between builds, or writing my own caching proxy to sit in between Hugo and Unsplash (similar to what I do with Spotify) - but that was more pieces to maintain.
Naturally, when creating something from scratch, I tried to fix every little problem I had with my current website. And I think I've been able to achieve that.
At the time of writing, I am employed by Torchbox, the creators of and primary contributors to Wagtail. Wagtail is 100% free and open source. They've not helped or contributed to my website in any way, and in fact may not even know this rewrite exists.
The core of the site is built using Wagtail, a CMS framework based on Django written in Python. Wagtail isn't like WordPress - you don't login to a configured instance, pick a theme and get going - the word "framework" is intentional. Wagtail gives you the building blocks to build custom page models with custom templates, and presents them in an easy-to-use CMS editor ready for content.
The most notable point about my use of Wagtail is that it's no longer static. Gone are the days of just having a directory of HTML files served up with a simple web server. Instead replaced by a database (PostgreSQL, in my case), some dynamic code and as much customization as you could want. I plan to keep drafting content locally in Markdown, but this way I can preview content much more easily, create more dynamic "blocks" and even publish secret pages.
Given I manage, maintain and host a number of Wagtail sites professionally, I wanted to be able to play around with similar things on my own website. See how Wagtail scales with my use case, have some fun with full-text search in PostgreSQL, or see how much caching is too much. None of that is possible with a static site - it's just point and go. Which for some people is perfect, but as someone who likes to tinker, it's a little too boring.
I did consider a few alternatives, namely something semi-static like fasterthanli.me. I ended up not going with that approach, in part because it was far more complex, but also because I was slowly not liking the idea of having a bunch of images and build artifacts committed into a git repository. Sure, git LFS exists, but so does Object Storage, and filesystems. If a platform like that existed out of the box, with the customization I wanted, I might have considered it, but it was much more of a project than I'm after right now - this website took long enough to build as it is!
An example of this is the search page. Rather than needing a submit button, it searches as you type (debounced, obviously). Results are fetched from the server, returned as HTML (reusing templates and logic from other pages) and inserted into the right part of the page. All this in just a handful of extra magical HTML attributes. Even cooler is the infinite scroll - whereby more elements are downloaded once you reach the end of the page. With HTMX, I can add an invisible element to the bottom of the results response which tells HTMX "When I'm in view, fetch the next page of results, and put them in my place". If that page too adds the same node, then voila, infinite scroll - without a react component in sight.
I get a surprising number of people asking me where my theme comes from, and if they can use it. I don't consider myself someone who is especially good at designing things. I just know what I like, and more importantly what I don't.
Last time, I merged a few themes I liked together to create a layout and aesthetic I wanted - and I was pretty happy with how it turned out. This time, I didn't need to go that far. I quite liked the general look of the site, with its simple navbar, tiny footer, and jumbotron-style homepage. It just needed a little refinement to make it look that little bit nicer.
The majority of the frontend is built using Bulma, a CSS framework which is much more lightweight than Bootstrap. Not only that, but it allows for far greater customization, and even removal of features I don't need. Bulma let me build out the basics pretty quickly, then use a few of the core components to assemble everything else. Unlike when I built my site with Bootstrap, I'm not using many of Bulma's pre-built components, and those I am are much simpler anyway. This makes customizing the UI to match my liking much easier, without having to fight against existing styles with tonnes of
Additionally, I finally added a native dark mode, or I guess light mode in this case. My previous website was in permanent darkness, but was done in a way which required a lot of custom styles and overrides to much of Bootstrap. This time, I've created a toggle, which remembers your preference and follows your system theme. There's a button in the navbar to switch theme at any time, and there's even a fancy animation as you do. The CSS itself does have some special casing, but thanks to a mixin I wrote it's very obvious when something is done for dark mode, making maintenance easier.
As with basically everything else, my website runs on my own server. In this case, a pretty small VM in Vultr. The VM handles hosting the website, database, analytics, comments, and a few other tiny services which I should really put somewhere else. Keeping everything in 1 place makes maintenance simpler, and using a VPS rather than my home server means my website is more likely to stay up if I'm playing around with my home server.
The application itself runs in a custom-built Docker container, orchestrated by docker-compose. It's a setup I'm familiar with, and works well enough for my needs. Sadly it does involve a small amount of downtime during a deployment (single digit seconds) as docker-compose doesn't natively support blue-green deployments (unless you use Swarm). And of course, all of that is provisioned by Ansible. Sitting in front of Wagtail is Traefik, of course, which handles TLS termination, as well as a few other useful bits of middleware.
There's no CDN-level caching in front of the site. Every request is handled by Wagtail and render actual HTML. What caching there is is handled by Redis, which does a little selective caching sprinkled in on certain elements like the navbar, but everything else is rendered at request time. This sadly has meant that response times have gone up, by a reasonable amount, but my hope is that it's not by a noticeable amount. For now, the site isn't scaled super high, but if it needed to in future, it wouldn't be too difficult.
Currently, the deployment process is manual. I don't imagine having to update the code especially often, in which case connecting to the server and running
docker-compose pull && docker-compose up -d isn't much of a strain. I'd like to eventually migrate to something more automated, and perhaps more powerful - but for now this works perfectly. When commits are pushed, CI automatically builds and tests the site before building the final container and pushing it to my container registry (backed by Backblaze B2).
As for the code, the primary copy lives on my GitLab server, but I also mirror it to GitHub for easier consumption. Just the CMS is there, not the content for obvious reasons. But if you're interested in seeing how certain things work, dive right in!
Well, Me. Who else would it be? You?
I started working on this new rewrite earlier this year, just after the launch of Wagtail 3.0, to act as a milestone and avoid an unnecessary extra upgrade. I've intentionally tried to avoid getting too sidetracked by other side projects, to keep things moving forwards. It didn't always work, but I think I've done fairly well.
It took a few months to develop the site, plus more time than I care to admit to import the content over. Yes I'm sure I could have automated it, but I wanted to make sure everything was formatted correctly and set up in blocks nicely. After lots of sanity checking, and a few reviews, it's all up and working as expected.
The site has actually been deployed for several weeks now, hidden on a subdomain behind basic auth. This way, I can get everything up and working ready to go, and just switch over some routes in Traefik to make it live. Today is the first day it's actually live and serving traffic. If you're reading this, it's clearly working great! You probably can't see a huge amount of difference, but there are definitely quite a few. Take a look around, there are some nice quality of life improvements to be had!
And now the website is done, I can get back to writing the content to populate it. Come along for the ride...
Share this page