The problem

For a long time I favoured Wordpress + nginx + MariaDB as the backend for personal web projects like this. Recently, I decided to switch to static pages in an effort to simplify (hah!) my workflow and reduce load on my server.

This was driven by a few things.

Cost

Yes, they are both free if you host your own. I used to host my WordPress instance on a cloud VPS, but with better things to spend my money on I decided to bring it “in-house”, making use of the excellent Docker manager feature in my Unraid NAS machine.

I would have continued with a locally hosted WordPress, were it not for…

Reliability

Keeping the various containers talking to each other was a nightmare. Migrating my content from the old installation never went cleanly and required lots of manual clean-up, particularly in the media library. Unraid whoopsies (mostly user-error) would break file & folder permissions for the web server container, and bad upstream Docker builds would cause irrecoverable damage to the database installation.

I persisted for some while after switching out MariaDB for MySQL, but eventually the database bugs re-occurred and I decided to throw the towel in. I can live with mistakes I’ve made causing rework, but when you’re pulling from mainline/stable upstream repositories and stuff is breaking, it’s more effort than it’s worth.

No doubt most of this was my own ineptitude, but it was just taking up too much of my…

Personal time

Maintaining and securing a WordPress installation can be time consuming. Even when it was working well on my cloud machine, I felt like I spent as much time looking after the various plug-ins, themes, database connections, analytics options, et cetera et cetera, than actually making content!

Worrying about security also led me to overburden my Wordpress installs with “helpful” plug-ins - the sort that would help you with SSL, real-time threat monitoring, etc. After hearing about a couple of undisclosed vulnerabilities in the plug-ins that were supposed to be protecting my stuff, I realised that I couldn’t trust them. They were also seriously slowing the site down.

Speed

What little content I had was poorly optimised, and moving to self-hosting also required some thinking about how to properly serve that content within the limits of a 100Mbps upload. Not only that, but my server was already working full duty as a CCTV NVR, Plex Media Server, ADS-B receiver, Valheim server, Virtual Machine host, and whatever else I could throw at it.

The answer to this problem had to have low overhead, be simple to get started with, and easy to work with going forward. I think I managed to tick 2/3 of those boxes…

The solution

Computing overhead

As far as content rendering goes, static site generators are undoubtedly quicker than a full-featured CMS. If I’m being really honest, I didn’t need all the functionality and complexity of WordPress. This is a hobby site, not a business or a popular blog.

After some trial and error, Hugo won the day. I dabbled with Ghost (a database-driven WordPress alternative) and Grav (a flat-file CMS) but both still had a level of complexity above my needs.

Being supremely lazy, I liked that Hugo had reliable Docker images available in the community, its own in-built web server for testing in dev, and a really low footprint on my system. Hugo only runs when you generate the content, and for a simple site like this that only takes a few hundred milliseconds.

Nothing on this environment is directly exposed to the Internet. To actually serve the site, I went for Nginx over Apache simply because it’s what I’m used to. I use the Nginx proxying features through a well-known front-end interface, which helps me serve multiple domains and implement basic access control really easily, whilst automating things like SSL-certificate provisioning.

Local proxying apparently also helps with speed of content delivery, but the real bottleneck here is my 100Mbps upload. So the final piece in the puzzle is a reputable global CDN, that also happens to be really friendly to hobbyists.

Speed

By CDN, I mean CloudFlare. With appropriate allow-lists in place, my proxy server only serves content to the CloudFlare CDN, which in turn serves it to you.

CloudFlare also helps accommodate my ever-changing IP address. They have a fast, global DNS network with DDNS support, so even when my lease renews they can keep the site cached & viewable until the DNS records are updated.

Simplicity

Ah, the elephant in the room. You see, Hugo doesn’t have an elegant (convoluted?) built-in WYSIWYG editor like WordPress. Almost all of your content is written in Markdown, which is a nice human-readable sort of source file that can be readily converted into HTML.


### In markdown you do headings like this
And make stuff **bold** like this.

Is this a huge barrier to entry? Not really. In a plain text editor, Markdown still has great readability. I’m using VS Code, which helpfully adds highlights & emphasis to things like typography styling or link syntax.

Once I got over the initial hurdles with Hugo - learning about content structure, theming & templates, and getting my Nginx configuration correct, actually writing content has become a breeze. Editing a simple text file and dumping it in the working directory is so much easier than logging in to WordPress and getting bogged down in customisation.

4/10 for learning curve, but 10/10 for speed once you get started.


How-To?

Sorry, I’d love to but I’m simply not qualified. If I gave instructions and your box ended up getting pwned, I’d feel bad. But you can read more about the stack including particular Docker images used below. They all have excellent community help available to get started:

  • klakegg/hugo static site generator docker image
    • This generates HTML from my markdown source files.
  • panr-hugo-theme-terminal
    • The theme I use.
  • Linuxserver.io Nginx web server docker image
    • This serves the web site on my internal network
  • Nginx Proxy Manager docker image
    • This acts as a proxy between the Internet and my internal network, and also manages SSL certificates.
  • Cloudflare DDNS docker image
    • This updates the Cloudflare DNS network with my dynamic IP address, allowing me to add a second proxy layer using the Cloudflare CDN.