One of the most common architecture questions I get from teams: "Should we use Kubernetes, ECS, or go serverless?" The honest answer is always the same — it depends. But "it depends" isn't useful unless you know what it depends on. Here's the decision framework I use after running all three in production.
The Three Contenders
Let's level-set on what we're actually comparing:
- Kubernetes (EKS on AWS) — A container orchestration platform that gives you maximum control over scheduling, networking, service mesh, and deployment strategies. You own the cluster, the add-ons, and the operational complexity.
- ECS (Elastic Container Service) — AWS's native container orchestrator. Tightly integrated with IAM, ALB, CloudWatch, and the broader AWS ecosystem. Fewer knobs to turn, but fewer footguns too.
- Serverless (Lambda) — No servers, no clusters, no containers to manage. You deploy functions, AWS handles everything else. You pay per invocation and duration.
Decision Factors
The right choice depends on a handful of concrete factors. Here's how I think about each one.
1. Team Size and Operational Maturity
This is the factor most teams underestimate.
- Kubernetes requires dedicated platform engineering capacity. Cluster upgrades, node group management, add-on versioning (CoreDNS, kube-proxy, VPC CNI), observability stack, and security patching — these aren't optional. If your team is under 5 engineers, K8s will consume a disproportionate amount of your bandwidth.
- ECS is the middle ground. You still manage task definitions, services, and capacity providers — but AWS handles the control plane, scheduling, and most of the cluster lifecycle. A team of 2–3 can operate ECS comfortably.
- Serverless has the lowest operational overhead. No patching, no scaling decisions, no cluster lifecycle. A single engineer can operate a production Lambda workload. The trade-off: debugging distributed Lambda-based systems requires solid observability habits.
2. Workload Shape
The nature of your workload should heavily influence your choice.
- Long-running processes (web servers, background workers, WebSocket connections) — Kubernetes or ECS. Lambda has a 15-minute timeout and isn't designed for persistent connections.
- Bursty, event-driven workloads (API handlers, file processing, scheduled jobs) — Serverless excels here. Scale to zero when idle, scale to thousands of concurrent executions in seconds.
- Stateful workloads (databases, message brokers, ML training) — Kubernetes with StatefulSets, or managed services (RDS, MSK, SageMaker). Don't try to run stateful things on Lambda.
- Multi-service microservices with complex networking requirements (service mesh, mutual TLS, traffic splitting) — Kubernetes gives you the most flexibility. ECS supports Service Connect and Cloud Map, but the ecosystem is smaller.
3. Cost Model
Each option has a fundamentally different cost structure:
- Kubernetes (EKS) — $0.10/hour for the control plane (~$73/month) plus EC2/Fargate costs for worker nodes. You're paying whether traffic is flowing or not. At scale, dedicated EC2 instances with Reserved Instances or Savings Plans are typically the cheapest per-unit compute.
- ECS on Fargate — No control plane fee. Pay per vCPU-hour and GB-hour for running tasks. More expensive per unit than EC2, but no capacity management. ECS on EC2 gives you the same cost profile as K8s worker nodes.
- Serverless — Pay per request and per GB-second of execution time. At low traffic, effectively free (generous free tier). At high, sustained traffic, can become more expensive than reserved container capacity.
The crossover point: In my experience, Lambda becomes more expensive than containers somewhere around 1–2 million requests/day with moderate execution duration. Below that, serverless is almost always cheaper. Above that, run the numbers.
4. Deployment Complexity and Velocity
- Kubernetes — Rich deployment strategies (canary, blue/green, progressive rollouts with Argo Rollouts or Flagger). GitOps with Argo CD or Flux. Maximum flexibility, but you build and maintain the pipeline.
- ECS — Native blue/green and rolling deployments through CodeDeploy or the ECS deployment circuit breaker. Less flexibility than K8s, but works out of the box.
- Serverless — Deploy in seconds. Lambda versioning and aliases support traffic shifting. But testing locally is harder, and the feedback loop for infrastructure changes can be slow when you have many functions.
5. Ecosystem and Portability
- Kubernetes — Cloud-agnostic (in theory). The CNCF ecosystem is massive: Helm, Kustomize, service meshes, policy engines, secrets operators. If multi-cloud or hybrid is a real requirement (not a hypothetical one), K8s is the only serious option.
- ECS — AWS-only. Deep integration with the AWS ecosystem is a strength if you're all-in on AWS, but there's no exit path to another cloud without rewriting your orchestration layer.
- Serverless — Highly vendor-specific. Lambda functions themselves are portable code, but the surrounding infrastructure (API Gateway, EventBridge, Step Functions) is deeply AWS-native. That said, if you're already committed to AWS, this is a feature, not a bug.
My Decision Framework
Here's how I recommend teams make this choice:
| Choose this… | When… |
|---|---|
| Serverless (Lambda) | Small team. Bursty or low-traffic workloads. Event-driven patterns. You want zero operational overhead and fast time-to-market. Budget is tight and traffic is unpredictable. |
| ECS (Fargate or EC2) | Mid-size team. Long-running services. You're all-in on AWS and want tight integration without K8s complexity. Steady traffic that justifies always-on compute. |
| Kubernetes (EKS) | Larger team with platform engineering capacity. Multi-cloud or hybrid requirements. Complex networking needs (service mesh, traffic policies). You need the CNCF ecosystem. Multiple teams deploying independently to the same cluster. |
They're Not Mutually Exclusive
The most effective architectures I've operated use multiple compute models. In my current organization, we run:
- EKS for the customer-facing microservices platform — multiple teams, service mesh, GitOps with Argo CD
- ECS Fargate for internal tools and batch processing — simpler operational model, no cluster management
- Lambda for event-driven workloads — ETL triggers, S3 event processors, scheduled jobs, API backends with low/unpredictable traffic
The key insight: choose the compute model per workload, not per organization. Don't force a streaming data pipeline into Lambda because "we're a serverless shop," and don't spin up an EKS cluster for a cron job that runs once a day.
Common Mistakes I've Seen
- "We need Kubernetes for everything." I've lived this one firsthand (more on that below). The cluster maintenance tax is real and relentless. If your team can't absorb it, you're not running Kubernetes — Kubernetes is running you.
- "Serverless can't handle our scale." Lambda can handle thousands of concurrent requests. The issue is usually cold starts or execution duration limits — not scale. If your functions run under 30 seconds and your traffic is bursty, Lambda handles it fine.
- "ECS is just worse Kubernetes." ECS is simpler Kubernetes. For teams that don't need service mesh, custom controllers, or multi-cloud, that simplicity is a feature. Less YAML, fewer moving parts, fewer 3 AM pages.
- "We'll need multi-cloud eventually." In 4 years of leading cloud infrastructure, I've seen exactly one team that actually needed multi-cloud. Don't pay the K8s complexity tax for a hypothetical future requirement.
A Real Story: When Kubernetes Became Unsustainable
While Kubernetes remains the right choice for our multi-team customer-facing platform, not every workload in the organization warranted that complexity. This is the story of one that didn't.
I managed a lean 3-person DevOps squad — myself included — responsible for multiple platforms running on AWS across different architectural patterns: serverless, containerized, and traditional compute. Among these were two EKS clusters running on RAM-shared subnets — VPC subnets shared from the original landing zone via Resource Access Manager. The clusters used Argo CD for GitOps deployments, and the Kubernetes operation alone demanded constant attention: cluster upgrades (EKS drops support for older versions on a fixed cadence — you can't defer indefinitely), node group management, add-on versioning across CoreDNS, kube-proxy, and the VPC CNI, Argo CD maintenance, and security patching. All of this on top of the other platforms we were already supporting.
Then one of the three resigned.
Overnight, it was just me and one other engineer responsible for the same operational surface area — not just the EKS clusters, but everything else we were running. Cluster upgrades, add-on patching, node group rotations, incident response — none of that work disappeared. It just landed on fewer shoulders alongside all the other platform responsibilities. The bus factor was now one — a single resignation away from unmaintained production infrastructure.
Around the same time, an organizational constraint forced a migration of the AWS account into a different landing zone. This wasn't optional — it was a compliance-driven restructuring of the account topology. EKS clusters cannot be moved between accounts. They have to be rebuilt from scratch — new VPCs, new IAM roles and IRSA trust policies, new node groups, new add-on configurations, and full re-registration of workloads with Argo CD. There's no shortcut.
I made the call: instead of rebuilding two Kubernetes clusters in the new landing zone with a two-person team, I rearchitected the workloads onto ECS Fargate.
The reasoning was straightforward:
- Two engineers cannot sustainably operate two Kubernetes clusters — especially when those clusters aren't our only responsibility. EKS isn't "set and forget" — even with managed node groups and Karpenter, you still own upgrade cycles, add-on compatibility matrices, and IRSA policy management. With the rest of our platform portfolio already stretching us thin, one more resignation would leave a single engineer responsible for everything — an untenable position.
- The account migration was a forcing function. I had to rebuild the compute layer regardless. Rebuilding into EKS would mean weeks of cluster bootstrapping, IAM re-wiring, and Argo CD re-integration — for workloads that didn't need Kubernetes-specific capabilities. ECS Fargate services, backed by Terraform and GitHub Actions, could be stood up faster because there's no cluster lifecycle to bootstrap at all.
- The workloads didn't justify the Kubernetes tax. No service mesh. No custom controllers or operators. No multi-cloud requirement. No complex scheduling constraints. The microservices needed container orchestration with automated deployments — ECS with GitHub Actions could deliver that directly. I considered keeping a lightweight management cluster solely for Argo CD, but that still meant maintaining an EKS cluster — the very operational burden I was trying to eliminate.
I completed the migration in significantly less time than rebuilding the EKS clusters would have taken. More importantly, we could operate the new ECS infrastructure without burning out. We got our weekends back. Cluster upgrade anxiety disappeared. The cognitive load dropped to a level that matched our actual capacity.
The lesson: Kubernetes isn't just a technology decision — it's a staffing commitment. If you can't sustain the team size that K8s demands, you're accumulating operational debt that compounds with every cluster upgrade you defer and every security patch you postpone. Sometimes the right architecture decision is the one that lets your team sleep.
TL;DR
Start with the smallest operational footprint that meets your workload requirements. For most teams, that means: serverless for event-driven and low-traffic workloads, ECS for steady-state services where you want AWS-native simplicity, and Kubernetes only when you genuinely need the ecosystem, the portability, or the multi-tenancy model.
The best compute platform is the one your team can operate confidently at 2 AM without calling in reinforcements.
Running all three in production has taught me that the "best" option is always context-dependent. Match the tool to the workload, not the hype cycle.