We've onboarded clients running a three-service application — API, worker, PostgreSQL — on a self-managed Kubernetes cluster administered by one engineer who learned Kubernetes six months ago specifically for this project. The cluster runs on two t3.medium nodes. It has no node autoscaling, no network policies, a manually renewed TLS certificate, and an ETCD backup that has never been tested. The engineer spends roughly 4 hours a week keeping Kubernetes alive instead of building product.
Kubernetes is a powerful tool. It's also the most common form of infrastructure complexity that early-stage teams import before they have any use for it. This article is an honest attempt at a framework for when K8s actually earns its operational overhead — and what to run instead.
What Kubernetes actually costs you
Before the "when to use it" discussion, it's worth being explicit about the overhead. Running Kubernetes — properly — requires:
- Cluster management: upgrades (Kubernetes releases security patches on a ~quarterly cadence; you have 12–14 months before a version is end-of-life), node pool rotation, control plane health
- Networking: CNI plugin selection, network policies if you care about pod-to-pod traffic isolation, service mesh if you need mTLS or fine-grained observability
- Security posture: RBAC configuration, pod security standards, image scanning, secrets management (Kubernetes secrets are base64-encoded, not encrypted, without additional tooling)
- Operational knowledge: debugging container scheduling failures, understanding resource requests vs limits, diagnosing OOMKills, reading controller logs when a deployment is stuck
On a managed service like EKS, GKE, or AKS, the control plane is managed for you. The rest is still yours. That's not an argument against Kubernetes — it's an argument for being deliberate about when the capabilities you're getting justify the overhead you're taking on.
Stage 1: Pre-product-market-fit (under 5 engineers)
At this stage, your infrastructure job is to keep the application running and not slow down the people building it. That's it. Kubernetes almost certainly doesn't help with either goal.
What works instead:
- Managed container platforms: Render, Fly.io, Railway, or AWS App Runner handle deployment, scaling, TLS, and secrets management with near-zero configuration. You push a container; it runs. Pricing is reasonable at this scale.
- PaaS for databases: RDS, Supabase, or PlanetScale. Not self-hosted PostgreSQL, not a StatefulSet you're managing yourself. The cognitive load isn't worth it.
- GitHub Actions for CI: Simple, well-documented, native integration with your code. You don't need a self-hosted CI runner at this stage.
The argument you'll hear for K8s at this stage: "we want to be cloud-agnostic." Cloud-agnosticism at 4 engineers building an MVP is a distraction. You're not switching clouds. And if you somehow need to, the migration cost of moving off App Runner is an afternoon, not a month.
Stage 2: Early scale (5–20 engineers, product-market-fit found)
This is where teams start feeling the limitations of managed platforms. The triggers that actually indicate you might be ready for Kubernetes:
- More than 8–10 distinct services with different scaling behavior — some need to scale out fast (real-time API), some run as long-lived batch jobs, some are ML inference endpoints that need GPU access
- Workloads that managed platforms can't accommodate — custom hardware requirements, long-running jobs, sidecar container patterns
- Multi-tenant isolation requirements — namespace-level isolation per customer or environment that you're currently solving with separate accounts or VPCs
- An engineer who knows Kubernetes — not learned-it-for-this-project, but has run it in production before and knows where the bodies are buried
The last point matters more than most teams acknowledge. Kubernetes's operational model is unfamiliar. Debugging a pod that won't schedule because of a resource request misconfiguration is not intuitive the first time. If nobody on the team has production Kubernetes experience, you will pay a steep learning tax at the worst possible time — during an incident.
What works instead if you don't meet these triggers yet: ECS on Fargate is underrated. No nodes to manage, native AWS integration, task definitions are simpler than Kubernetes manifests, and the scaling model is straightforward. Teams at 10–20 engineers with a primarily AWS workload can get most of what they'd get from EKS without the operational overhead. We've run production workloads at significant scale on ECS and there are very few things we couldn't do.
Stage 3: When K8s is genuinely the right answer (20+ engineers)
Kubernetes starts earning its overhead when:
- You have enough workload diversity that the ability to colocate heterogeneous services on shared nodes — and have the scheduler optimize placement — generates real cost savings
- You need environment parity between local development, staging, and production (Kubernetes manifests + Helm achieve this; ECS task definitions are harder to replicate locally)
- Your compliance requirements push toward audit-first infrastructure — Kubernetes audit logging, RBAC, and OPA/Gatekeeper give you controls that managed platforms don't expose
- You're building a platform that other teams deploy to — the golden path concept only works at scale if the underlying abstraction layer can accommodate diverse workloads without special-casing every one
At this point, use a managed Kubernetes service. EKS, GKE, or AKS. Self-managed Kubernetes (kubeadm, kops, k3s in production) is almost never the right answer for a product company. You are not in the business of running Kubernetes. You're in the business of running your product on Kubernetes. Let the cloud provider handle the control plane.
The managed Kubernetes setup that actually works
If you've decided K8s is right for your stage, the setup patterns that hold up in production:
- GitOps from day one. ArgoCD or Flux watching a Git repository for cluster state. No
kubectl applyin CI scripts. Every change is a Git commit, which means every change is auditable and reversible. - Helm for application packaging. Helm charts parameterize the differences between environments cleanly. Kustomize is fine for simple cases; Helm handles the complexity that always shows up later.
- Node autoscaling. Cluster Autoscaler or Karpenter. Your node count should respond to load, not be manually managed. Karpenter (AWS) specifically has a better bin-packing algorithm that often yields 20–30% better node utilization than Cluster Autoscaler.
- Resource requests and limits on everything. Without them, a single misbehaving pod can starve neighbors. We set requests conservatively and limits generously, then tune based on 2 weeks of actual utilization data.
- Network policies enforced. Default-deny ingress and egress at the namespace level, explicit allows for the communication patterns you actually need. A pod compromise in one namespace should not be able to reach every other service in the cluster.
The honest conversation we have with clients
When a team asks us "should we use Kubernetes?" our first question is: what problem are you trying to solve? The answers are usually one of three things:
- "We want to be able to deploy independently per service." Kubernetes solves this. So does ECS, so does Fly.io, so does a well-structured GitHub Actions workflow. The deployment independence problem doesn't require Kubernetes.
- "We want to scale our API tier without scaling everything." Also solvable without Kubernetes. This is literally what autoscaling groups on EC2 or Fargate tasks do.
- "We're running 15+ services, we need environment parity across dev/staging/prod, and we have an engineer who's shipped Kubernetes in production." Now we're talking. Kubernetes is probably the right answer.
If you're in category 1 or 2 and you pick Kubernetes, you'll be maintaining a complex distributed system to solve a problem that didn't require it — and the complexity will slow you down at the exact moment when speed matters most. The right tool for the current stage of your problem is almost always the boring, well-understood one that you can operate at 2am with a clear head.
We say this as a team that manages Kubernetes clusters every day and has run K8s in production across dozens of clients. It's a good tool. It's not the right tool for every stage.