Replacing Docker Compose with K8s: Was It Actually Worth It?

|4 min read|

A look back at migrating a homelab from simple Docker Compose stacks to a full GitOps-driven Kubernetes cluster. The pain points, the over-engineering, and why I don't regret it.

Yoga Novaindra

Author

Let me start with a confession: for years, I ran my entire homelab on a massive, sprawling docker-compose.yml file. It was a chaotic text document that spanned hundreds of lines. When I wanted to update an image, I manually edited the file and ran docker compose up -d. When I needed a new service, I copied and pasted a block, changed the ports, and hoped for the best.

It worked. It worked surprisingly well.

So when I decided to rip it all down and build a GitOps-driven Kubernetes cluster from scratch, people asked the obvious question: Why? Isn't that complete overkill for a homelab?

The short answer is yes, it absolutely is overkill. The long answer is more complicated. Here is an honest retrospective on moving from Docker Compose to Kubernetes—the pain points, the unnecessary complexity, and the moments where K8s genuinely proved its worth.


The Pain Points: Where K8s Made Me Question My Sanity

If you're coming from Docker Compose, Kubernetes feels like moving from a bicycle to a commercial airliner. Both get you from A to B, but one requires a 600-page manual just to start the engine.

1. Storage is Suddenly Hard

In Docker Compose, persistent storage is beautifully simple. You map a host directory to a container path: - /mnt/data/plex:/config. Done.

In Kubernetes, you can't rely on local host paths if you want a highly available cluster. If a node dies and a Pod moves, its data needs to move with it. This led me down the rabbit hole of configuring CephFS distributed storage and RustFS. Getting Plex and the Arr suite to play nicely with distributed storage without locking up the database was a weekend I'll never get back.

2. Networking Becomes a Layer Cake

Docker Compose puts everything on a bridge network. Want to expose it? Map a port to the host.

Kubernetes networking requires you to understand Services (ClusterIP vs. NodePort), Ingress controllers, and LoadBalancers. Because I run bare-metal, I couldn't just rely on AWS or GCP to provision a load balancer for me. I had to learn how to deploy MetalLB to handle Layer 2 IP allocation, feed that into Traefik for ingress routing, and eventually tie it all together with the Cloudflare Operator for zero-trust tunnel routing.

3. Secret Management

In Docker Compose, I had an .env file. It was bad practice, but it was easy.
In a public GitOps repository, you can't just push API keys and database passwords to GitHub. I had to implement Sealed Secrets, write the manifests, encrypt them locally with kubeseal, push the encrypted blobs to Git, and let the cluster decrypt them on the fly. It is vastly more secure, but the friction for adding a simple database password went from zero to ten.


The Turning Point: Where K8s Actually Won

If the first month was just fighting the platform, the second month was when I started to see the light. Once the foundation was laid, the day-to-day management of 60+ applications across 15 namespaces became almost entirely hands-off.

1. True GitOps Automation

This was the absolute game-changer. I deployed ArgoCD and pointed it at my repository. Now, my Git repository is the absolute source of truth.

If I accidentally delete a namespace in the cluster, ArgoCD notices the drift and instantly recreates it. If I want to deploy a new service, I don't SSH into a server; I just merge a pull request. ArgoCD sees the new manifest and handles the deployment. This level of declarative, self-healing infrastructure is something Docker Compose simply cannot do natively.

2. Automated Dependency Management

In my Compose days, updating apps meant checking release notes, changing tags, and pulling images.
Now, I run Keel and Renovate. Keel automatically watches my container registries and performs rolling updates on deployments when a new image tag drops. Renovate handles my Helm chart versions. My cluster stays up to date while I sleep.

3. A Centralized Brain (Observability)

When a Docker Compose stack breaks, you are usually exec-ing into containers and tailing logs manually.
Because Kubernetes is highly structured, implementing a proper observability stack was surprisingly smooth. I deployed the Prometheus, Grafana, Loki for centralized logging, and Tempo for tracing. Now, I have a unified dashboard that tracks the health, CPU usage, and network flow of every single Pod in the cluster.


Was It Worth It?

If your goal is just to run Plex and Nextcloud on a spare Intel NUC, then no, Kubernetes is not worth the headache. Docker Compose will serve you perfectly fine, and you will get your weekend back.

But if your goal is to learn modern, cloud-native infrastructure, or if you're managing a growing number of services (I'm currently at 60+ apps), Kubernetes is a revelation. It forces you to build things correctly. It abstracts the underlying hardware, enforces declarative configurations, and gives you a platform where automation actually works.

I spent weeks over-engineering this setup, wrestling with CephFS, fighting Traefik routes, and learning ArgoCD sync phases. But now? The cluster practically runs itself. And that makes the initial pain completely worth it.

© 2026 Yoga Novaindra Powered by Ghost