read time minute to read


Kubernetes for the Homelab

Kubernetes for the Homelab

  • mdo  jonas
  •   HomeLab
  •   January 19, 2025

So now the madness begins. In theory, I have everything set up to host a small web server, specifically for this blog. In fact, I initially installed Bludit and Nginx to host a basic blog. However, where’s the fun in that? Let’s start to over-engineer!

This blog post outlines my journey of building a wildly over-engineered system to host my blog. If you ever find yourself asking, “Why are we doing this?” the answer is likely, “Because we can.” This is about solving non-existent problems and automating tasks where the automation takes longer to build than the manual work itself.

The ultimate goal is to build a system where my blog can seamlessly adapt to changes, maintain near-perfect uptime, and scale for future needs. Let’s dive in!

The Vision

To give this project context, let’s look at the use case: I want to host a blog on my Raspberry Pi (RPI). Every time I make a change—such as installing a new plugin or tweaking the design—the system should update automatically without downtime. My readers should never notice any interruptions.

For example, I might decide to install a plugin that improves the blog’s RSS feed. With this setup, I can ensure that the blog updates itself while maintaining its current state for visitors. Additions like load balancing, automatic scaling, and resource distribution enhance the blog’s reliability.

Currently, I only have one RPI, but soon I’ll integrate more. If one device fails, the others will take over seamlessly. My dream is to achieve a 99.99% uptime for the blog. Realistically, my ISP’s nightly disconnects limit this, but within my local network, the blog infrastructure should achieve this goal.

Kubernetes

The first major decision was to use Kubernetes to manage the blog infrastructure. While Kubernetes might be overkill for a personal blog, it’s a fun and educational exercise. Plus, it offers scalability and redundancy as I expand the cluster.

Currently, the cluster runs on a single RPI, but I plan to add more. Kubernetes will ensure that if one RPI goes down, another can take over, keeping the blog online. For a three-node cluster, I’ll need two more RPIs. This setup will enable high availability, ensuring the blog remains accessible even during failures.

The RPI has an ARM processor, which caused far fewer issues than I anticipated. ARM architecture is already widely supported by major cloud providers, making this an ideal choice for Kubernetes experimentation. However, Raspbian OS doesn’t support Kubernetes easily, so I switched to Ubuntu.

Ubuntu enabled me to install MicroK8s, a lightweight Kubernetes stack that includes features like a basic dashboard and an NFS storage controller. The storage controller allowed me to integrate my NAS as persistent storage for the blog.

As you can see there is a lot going on. Thankfully everything is in good order. Every shown resource can be deeper inspected from the navigation bar on the left. Often it is easier to find the recourse via the dashboard than via the command line tools of kubernetes.

Challenges in Configuration

Setting up Kubernetes for a blog wasn’t straightforward. For example, the MicroK8s dashboard didn’t ship with an ingress controller by default, so I had to configure one manually. This step was essential to ensure the dashboard and blog were accessible over the network.

Similarly, integrating my NAS required configuring an NFS storage class. This allows my blog’s data—like uploaded images or cached pages—to persist across deployments. Without this, every update to the blog could wipe out its content!

Another challenge was balancing Kubernetes resource allocation. Since RPIs have limited processing power and memory, I had to carefully manage workloads. For example, deploying large Docker images or running resource-intensive tasks like a blog rebuild could cause the system to hang or fail entirely.

Version Control

Version control is vital for keeping track of changes to the blog infrastructure. For example, when I updated Bludit’s outdated Docker image (which hadn’t been updated in over two years), I needed a way to track and document those changes.

I installed Gitea, a lightweight Git server, to manage version control for my blog. With Gitea, I store Helm charts, Docker images, and configuration files. Each time I make a change, I push it to the repository. This ensures that I can roll back any issues and maintain a history of changes to the blog.

Gitea also supports GitHub Actions, which I use to automate the process of building Docker images and updating Helm charts. This way, every change I push to the repository automatically triggers an update to the blog. However, I discovered that running multiple Gitea runners on an RPI with limited RAM causes resource contention, leading to frequent build failures. For now, I only use a single runner to avoid this issue.

The boniclaud organisation on my gitea instance. I added a separate git account for the administrator and my development system. The blog’s configurations and adaptions (all plug-ins, the docker, etc.) are versioned in the bludit repository. Gitea only allows to publish packages to organisations or user, so the boniclaud organisation owns all helm charts and docker images.

Automating Deployments with ArgoCD

Manually deploying changes to the blog would be time-consuming, so I automated the process using ArgoCD. Whenever I update my Helm charts or Docker images in Gitea, ArgoCD automatically deploys the changes to the Kubernetes cluster.

For instance, if I add a new feature to the blog, such as a comment section, I update the repository, and ArgoCD handles the rest. It also monitors the blog’s deployment status, ensuring that it remains healthy.

Currently, I use a test blog for automatic updates. Once I verify that the changes work as expected, I manually update the live blog to avoid disruptions.

In the future, I’d like to integrate more advanced testing into this workflow. Currently, the health checks only verify that a web service is reachable. Adding automated tests to verify specific blog functionality (like plugin compatibility or theme integrity) would make the system even more robust.

Yes, everything should be green. But the out-of-sync projects are mainly the infrastructure to maintain the cloud. Often it is just easier to change stuff directly in kubernetes, see how it plays out and only then commit to the change. This way I am just a “sync apps” click away to have my cluster return into the checked in state. And there is a bug with Argo waiting for destroyed pods to report their health, that makes the health of gitea stuck in “progressing”…

Lessons Learned

Hosting my own blog using Kubernetes has been a challenging but rewarding experience. I’ve learned about modern cloud technologies, improved my DevOps skills, and built a system that’s both fun to manage and educational.

I now have a scalable, redundant system that can grow with my needs. However, I’ve also realized that over-engineering a system comes with its own set of challenges—especially when dealing with limited hardware like RPIs.

Current Setup

Here’s an overview of my blog’s current stack:

  • MicroK8s: Manages the Kubernetes cluster.
  • Gitea: Handles version control for the blog’s Helm charts and Docker images.
  • ArgoCD: Automates deployments and monitors the blog’s health.
  • Bludit: The blogging platform, running on a custom Docker image.
  • CloudflareTunnel: Exposes the blog to the internet securely.

Conclusion

This project started as a simple way to host my blog but quickly evolved into an exploration of over-engineering and modern cloud technology. While most of these tools are overkill for a personal blog, the experience has been invaluable—and a lot of fun.

As I continue to build out this system, my next steps will include expanding the Kubernetes cluster, integrating better testing workflows, and improving internet uptime. Stay tuned for future updates as I refine my BoniCloud setup!

homelab yggdrasil kubernetes


Powered by Bludit - Theme by BlThemes
© 2025 BoniClaud