← Back to blog
4 min read

From local execution to Docker to Kubernetes: what actually changed

I built an overengineered distributed system on purpose to practice decoupling, idempotency and Event-Driven Architecture. Then I containerized it with Docker and pushed it into Kubernetes later, not because it needed it, but because I wanted to learn orchestration safely. Kubernetes for a project with zero users is overkill, and that's exactly why my laptop was the right place to learn it so I could simulate errors and mess up as much as possible without colleagues panicking.

4 stages

The system simulates an e-commerce order process following Event-Driven Architecture: a .NET API publishes orders and retrieves orders' states, a Rust processor handles the events, RabbitMQ carries the messages between services and Postgres stores the order state.

Locally run services

The tech stack was chosen out of pure curiosity for Rust while .NET, Postgres and RabbitMQ were technologies I already knew. I started by designing the db for idempotency, correlation and dead letter retries reprocessing logic. Then I implemented the services while Rust was mostly AI-generated for me to learn the syntax and the development process. It ran perfectly end to end on my machine. Then I containerized it and that's where the interesting part started.

Containerized services

Standardizing the software for compatibility across machines and the cloud services required containerization. I wrote a Dockerfile for each service; localhost was removed and all the services became names within Docker. However, there were some issues: Docker Compose starts containers in order, but depends_on waits for start, not readiness, so the infrastructure services might be down and the application layer wouldn't work until a manual restart.

Minikube addition

To resolve the manual restart problem, I decided to add Minikube, but this choice came with a big trade-off: complexity and automated recovery over simplicity but manual management. The first deployment manifests were simple: no liveness or readiness probes and, importantly, no resource limits. I had to write Kubernetes deployments for each service, test and verify that both services and connections kept working; spoiler: they didn't at first. Having both Docker Desktop and Minikube, the latter required a copy of the images in its own environment for the pods to start. You may ask why I didn't use the Kubernetes provided by Docker, and it's a legit question: the answer is there was not enough storage space on my laptop.

Local Kubernetes Cluster on OrbStack

Here we are at the last step: removing Docker Desktop and Minikube in favor of OrbStack that is more lightweight and more efficient (in terms of consumed resources: CPU, RAM and disk) for my machine's OS. The migration was easy: after removing Docker and Minikube and cleaning the remaining files, the only two steps needed were building the images and applying the deployments. It changed the service provider but the engine was the same.

How I froze my own laptop

As the system was running, I started a stress test, but after a while my laptop froze and I restarted it. Digging into the issue, I found out that without limits a pod can consume as much CPU and memory as the node has, and on a local cluster the node is my laptop. I set the limits afterwards!

The honest results

Today the system runs as 4 pods: the .NET API, the Rust processor, RabbitMQ and Postgres. What actually changed:

The drawbacks are just as real:

For simplicity, I did not consider variable and secret management, and I'm planning to improve the project by securing it and adding a new service for monitoring.

The takeaway: Docker is enough until automated recovery is a pain and a cost; Kubernetes becomes worth it the day manual restarts and low uptime cost more than the complexity behind it. I would add Kubernetes again when I need automated recovery, scaling or multiple environments, for example a customer-facing application, not to run four services on my laptop for zero users!

Enjoy the rest of your day!