I used to write periodic homelab update posts but they’d go stale almost immediately. So instead, I’m just going to keep this page current. It’s the state of things as of the last updated date at the bottom.

Everything is managed via a private Git repo with FluxCD handling the GitOps side of things.


The Hardware

It’s a 4-node K3s cluster, all sitting under my desk. A mix of whatever I’ve picked up cheaply over the years:

  • Control plane: An old laptop with an Intel i5 (4 threads), 16GB RAM, running Ubuntu 24.04. It does its job.
  • Two x86 workers: Both around 8GB RAM, 4 cores each. One’s running Debian 12, the other Ubuntu 24.04.
  • A Raspberry Pi 4: 4GB RAM, running Raspbian. Mostly handles lighter workloads.

All in, that’s about 16 cores and ~35GB of RAM across the cluster. More than enough for what I’m running.

There’s also a separate dedicated NAS box running two 12TB Seagate Ironwolf drives in a ZFS mirror. The cluster mounts this over NFS.


Storage

ZFS on the NAS

The NAS runs ZFS with the two 12TB drives mirrored, so I get about 12TB usable with redundancy. I’ve split it into datasets so I can apply different policies:

  • nas/media - all the media, lz4 compressed
  • nas/important - the stuff I really can’t lose, with snapshots enabled
  • nas/k8s - where Kubernetes PVCs end up
  • nas/backups - what it says on the tin
  • nas/tmp - scratch space

Snapshots on the important stuff are handled by zfs-auto-snapshot. It’s not a proper backup strategy on its own but it’s saved me more than once.

How the cluster uses it

On the Kubernetes side, I use the nfs-subdir-external-provisioner to create a nfs-client StorageClass. Most apps just request a PVC and get a directory on the NAS automatically. There’s a second provisioner just for music storage (Navidrome, Picard, etc need access to the same music library).

Some apps that need better I/O use local-path storage directly on whichever node they’re scheduled on.

The cluster has 80+ PVs at this point. Media-heavy apps like Jellyfin and Immich mount the big ZFS datasets directly. The monitoring stack alone takes up about 60Gi (Prometheus, Loki, Tempo, Grafana, Alertmanager). Each database gets 8Gi via CloudNativePG.


How it all fits together

FluxCD

The whole thing is GitOps. I have a repo with a pretty simple structure:

acplpuss/ters/k3s/##OCnleusdtierre-clteovreylpceornfaipgp,liFclautxiobnootstrap,registrysecrets

Each app directory has a kustomization.yaml pointing at its namespace, Helm release, ingress, certs, and storage. Push to the repo and Flux picks it up. There are currently 39 HelmReleases running across the cluster.

Sealed Secrets

Since the repo contains secrets (database passwords, API keys, etc), everything is encrypted with Bitnami Sealed Secrets. I create a normal Kubernetes Secret locally, seal it with kubeseal, and commit the encrypted version. Only the controller running in the cluster can decrypt them. It works really well and means I don’t have to worry about what I commit.

Renovate

Renovate runs as a CronJob inside the cluster itself. It watches Helm chart versions and container image tags, then opens PRs when updates are available. Some things (like Tube Archivist) I’ve set to auto-merge. Everything else I review manually, but it means I’m never more than a PR away from being up to date.

Ingress and TLS

Traefik comes bundled with K3s so I use that for all the routing. Each app gets its own subdomain and cert-manager handles the TLS certificates. For local dev access I use mkcert.

Databases

Postgres is handled by CloudNativePG, which lets me run proper Postgres clusters as Kubernetes workloads. Immich, Gitea, Fittrackee, Wallabag, Matrix, and Paperless-ngx all get their own dedicated Postgres instance. Immich’s uses pgvecto.rs for its ML-powered photo search which is pretty neat.


What’s running

The dashboard

I use Homepage as a landing page. It shows all the services with live health checks and a cluster resource overview. It’s the first thing I check in the morning.

Media

  • Jellyfin for video and audio streaming
  • Navidrome for music (I use it with Subsonic clients on my phone)
  • Tube Archivist for archiving YouTube videos I want to keep (backed by Elasticsearch and Redis)
  • Podsync for converting YouTube channels into podcast feeds
  • Calibre-Web for ebooks
  • Picard for tagging and organising music metadata

Productivity

  • Miniflux for RSS - it’s fast and stays out of the way
  • Paperless-ngx for scanning and archiving documents. The OCR is brilliant, I just scan everything and search for it later
  • CryptPad for documents when I want end-to-end encryption
  • Wallabag for saving articles to read later
  • Trilium for notes and knowledge management
  • n8n for workflow automation

Self-hosted infrastructure

  • Gitea with Valkey for caching. It’s my own Git server and also serves as a source for FluxCD
  • Immich with Redis. This is probably my favourite thing running on the cluster - it’s a full Google Photos replacement and it’s genuinely good
  • Matrix (Synapse + Element) for chat, with bridges to WhatsApp, Telegram, Signal and LinkedIn so everything’s in one place

Smart home

  • Home Assistant for home automation

Tracking

  • Fittrackee for exercise/activity tracking
  • Speedtest Tracker for keeping an eye on my ISP
  • Koinsight for financial stuff

Monitoring

I went a bit deep on the monitoring stack. It’s probably overkill for a homelab but I use Kubernetes at work too so it’s good to have experience with the full stack.

The core is kube-prometheus-stack which gives me Prometheus and Grafana out of the box. On top of that:

  • Loki for log aggregation (single binary mode, keeps things simple)
  • Tempo for distributed tracing
  • OpenTelemetry Collector running as a DaemonSet, forwarding traces to Tempo
  • Uptime Kuma for uptime monitoring of all the services

For alerting, Alertmanager sends critical alerts to ntfy which pushes notifications to my phone. I’ve set up inhibition rules so I don’t get spammed - critical alerts suppress warnings, warnings suppress info-level stuff.

The Grafana dashboards I actually look at regularly are the cluster compute resources overview, pod health by namespace, and PV usage.


What’s next

Honestly this changes all the time but the things on my mind:

  • Wrapping more of the app deployments into Kratix Promises. I work on Kratix at Syntasso so this is partly dogfooding, partly because the Promise abstraction is genuinely a nice way to manage self-service deployments
  • Proper network segmentation. Right now everything’s flat on the same LAN which isn’t great
  • Better off-site backups. I’m technically doing off-site backups now using rclone to iCloud (I had 1.7TB sitting there doing nothing) but it’s hacky and I wouldn’t trust it as my only recovery option
  • Maybe a second cluster at some point for playing with multi-cluster tooling

Last updated: May 2026