This summer I moved every app I run off Azure and onto a Kubernetes cluster I manage myself. Not because Azure is a bad platform. It isn't, and there are plenty of situations where I'd still recommend it. I left because cost had started controlling every conversation I had about my own architecture. The bill was quietly making my technical decisions for me.
Here's what that looked like in practice. My apps ran on Azure Container Apps, with Azure SQL behind them and a container registry for each project. Per-service pricing sounds reasonable when you're looking at one service. It stops sounding reasonable when you add up what a real system needs.
Every environment was a line item
A typical app of mine is a REST API with a separate frontend. On Azure, those are two billed resources. One project was three: an API, a frontend app, and a database. Each project also carried its own container registry at a flat monthly fee, and one had a Redis cache whose cheapest tier can't be paused. The only way to stop paying for that Redis was to delete it. The bill tracked my architecture diagram, not my traffic.
Staging made the math worse. A real staging environment means duplicating everything: a second API, a second frontend, a second database. Adding one meant roughly doubling the cost of every app, so I went without. That's a compromise nobody should have to make just to keep hosting affordable.
The part that bugged me most is that cost control was actively making the apps worse. To keep the bill down, everything ran scaled to zero. When an app sat idle for a while, Azure shut it off, and the next visitor waited while the container spun back up. I was paying for hosting and still handing first-time visitors a loading spinner.
What I wanted to keep
Leaving a cloud platform doesn't have to mean going back to one app on one server and hoping for the best. The things Azure genuinely gave me (load balancing, replication, health checks, rolling deploys) are exactly the things I refused to give up. Kubernetes provides all of them on any hardware you point it at.
I'd also wanted to learn Kubernetes properly for a long time, and running it in production for my own apps was the honest way to do it. Reading about pod scheduling is one thing. Being the person who has to fix the cluster when a deploy goes sideways teaches much faster.
There was a control argument too. On Azure, unrelated projects ended up sharing infrastructure because of platform limits, which made tearing anything down risky. When I'm running the cluster, nothing changes unless I change it.
The stack I run now
Everything now runs on my own hardware at a hosting provider: 6 cores, 64 GB of RAM, RAID NVMe drives, and a price that never moves. On top of it:
- k3s, a lightweight Kubernetes distribution. About a dozen applications live in isolated namespaces. Public-facing apps run two replicas behind the cluster's load balancer, with health probes and rolling deploys. The same safety nets I had on Azure, minus the meter.
- Staging that costs nothing extra. Staging is just another namespace on the same box, running the same build that ships to production. The environment I couldn't justify on Azure now exists for every app that needs it.
- Cloudflare Tunnel in front of everything public. The server's firewall is completely locked down. All web traffic enters through an encrypted tunnel to Cloudflare's edge, which also terminates TLS. There are no open ports to scan.
- Cloudflare Zero Trust for admin surfaces. The Kubernetes dashboard and other internal tools sit behind access rules with named groups, so only the right people can even reach a login page.
- VPN for private access. A VPN connects my machines and infrastructure directly, so administration never has to touch the public internet.
The honest trade-off: security, patching, backups, and monitoring are my job now. Nightly database backups with restore drills, alert rules, runbooks for deploys and rollbacks. That's real work, and I took it on deliberately. It's also become part of what I offer clients, because the same setup works for their apps too.
And if I ever outgrow the box, that's what a cluster is for. Add a node, spread the load, keep going. Hardware turns out to be the cheap part.
The bottom line
My hosting costs a fraction of what it did, and I got more for it, not less. Staging environments exist now. Every app stays warm, so nobody waits on a cold start. The load balancing and replication I moved to the cloud for in the first place came along for the ride. And when I weigh an architecture change, the question is whether it's a good idea, not what it adds to the invoice.
This matters beyond my own apps. Hosting is part of what I build, and running infrastructure this way is how I keep costs predictable for the software I develop and host for clients. If your cloud bill has started making your decisions for you, let's talk.