Today I decided to do some maintenance on my various websites, which are built using a mixture of React, Hugo, and MkDocs, then packaged as Docker containers and deployed to a Kubernetes cluster.

The setup has grown gradually over time, but the basic idea is fairly simple.

The Deployment Pipeline#

Each site lives in its own repository on my Git server. When changes are pushed to the main branch, a Drone CI pipeline builds a new Docker image, pushes it to my container registry, and updates the relevant deployment on the Kubernetes cluster.

Most of the day-to-day management is handled through kubectl, although I also use k9s when I want a clearer view of what is happening inside the cluster. It makes checking pods, deployments, and logs much more pleasant than typing out long command lines all the time.

My main company website is a React-based site. From there, visitors can reach the other services and sites I run, including the interactive in-browser demos, the documentation portal generated with MkDocs, and this JKTDev site, which is generated with Hugo.

Streamlining the Workflow#

One of the reasons I built things this way is convenience. I can deploy changes simply by pushing code to Git, and within a few minutes the updated version is live. There is no manual copying of files, no running several deployment scripts by hand, and no need to remember a long list of steps every time I make a change.

Work smarter, not harder.

Today’s maintenance was mainly about putting everything into a good state again: rebuilding images, applying base image updates, checking that deployments were healthy, and making sure the sites were still behaving as expected. It is a setup that has worked well over the past few months and has helped reduce the amount of admin involved in keeping everything up to date.

Pragmatic Over-Engineering#

This is admittedly more infrastructure than a simple static website strictly needs. However, running this on a lean, self-hosted cluster gives me a highly consistent environment for deploying websites, interactive demos, technical documentation, and small internal tools without varying setups causing friction.

In front of the websites I also run HAProxy, which provides a single entry point and handles HTTPS termination. This keeps the public-facing side of the setup a little cleaner, with the individual services sitting behind it.

ChatGPT-Assisted Monitoring#

I also have an n8n instance running for some basic monitoring. Instead of a standard ping test that just checks if the server is awake, this workflow pulls down key public pages and uses ChatGPT to scan the actual page content for relevant phrases, missing sections, unexpected text changes, broken output, or signs that something has not rendered correctly.

That gives me a much more useful signal without needing to manually click through each site after every minor deployment. Static sites can change in small, unexpected ways between builds, especially where generated output, script hashes, timestamps, or formatting are involved. Looking for meaningful phrases and expected engineering content is a practical way to confirm the frontend is broadly healthy.

Hopefully, I have not jinxed it by writing that down.

Overall, the setup seems to be working well. It is not perfect, and I am sure there are plenty of small improvements I could make, but it gives me a practical, flexible way to run several small sites and services without turning web infrastructure maintenance into a full-time job.

For now, that is good enough.


Looking to automate business workflows, self-host applications, or build bespoke web tools? Find out about my software development services at Protoworks (NI).