Skip to main content
CloudJune 25, 202511 min read

Most Teams Running EKS Would Have Been Fine on ECS

Get technical support

AWS run day to day, not when it breaks

Where ECS is enough, where the Kubernetes API genuinely earns EKS, and what the upgrade clock, the add-on stack and the AWS-only wiring cost you either way.

The case for EKS, made properly

Some teams should be running EKS, and the tell is almost always that they are already using the parts of Kubernetes that have no ECS equivalent.

Custom resource definitions and the operators that reconcile them are the clearest example. If a platform team ships CRDs that application teams consume, the Kubernetes API is your integration surface and nothing in ECS replaces it. Admission webhooks that enforce policy at the API server are another. So is scheduling that depends on affinity rules, taints and tolerations, or topology spread constraints, because ECS placement is a much narrower language and cannot express most of that. Add a fleet of clusters, a GitOps controller reconciling them, and internal tooling written against the Kubernetes API, and ECS is not in the running at all.

The hiring argument is real too. Kubernetes experience transfers between employers and ECS experience mostly does not, which engineers understand perfectly well when they choose what to work on.

The Kubernetes extension model is what you are buying. Everything below is about whether you are going to use it.

What ECS actually is

Capacity in ECS means EC2 instances you manage yourself, Fargate, ECS Managed Instances where AWS handles provisioning, patching, scaling and maintenance of the machines, or on-premises servers registered through ECS Anywhere. There is no additional charge for ECS orchestration on the EC2 launch type, so you pay for the compute and nothing for the scheduler on top.

The primitives are small enough to hold in your head. A task definition is the blueprint, a task is a running instance of it, a service keeps a desired count of tasks alive, and a capacity provider decides what they run on. The part that matters is that there is no second model underneath the first one.

The feature set has also stopped being thin, which is where older comparisons go stale. Service Connect provides service-to-service communication as ECS configuration, with a proxy container in each task doing round-robin load balancing and outlier detection, and optional TLS backed by AWS Private CA. Blue/green deployment is native to the ECS service scheduler now rather than assembled out of CodeDeploy, with a bake time during which both revisions run and lifecycle hooks that invoke a Lambda function or pause until you call ContinueServiceDeployment.

Where ECS runs out of road

ECS has a ceiling, and being fair to it means naming that too.

ECS has no extension point. There are no custom resources, no operators, no admission control. When you need behaviour the service does not implement, you build it outside ECS in a Lambda or a pipeline step and you own the reliability of that glue, where Kubernetes would have let you write a controller that lives beside the workload and reconciles it continuously.

Scheduling is narrower. On the EC2 launch type you get placement strategies such as spread and binpack alongside placement constraints, and AWS is explicit that strategies are best effort while constraints are binding and can leave a task stuck in PENDING. On Fargate, neither is supported at all, and ECS Managed Instances has constraints but no strategies.

Service Connect stops at the namespace boundary too, and AWS documents that tasks in other namespaces, tasks not configured for Service Connect, and applications outside ECS cannot resolve its endpoint names. Those four primitives are the whole vocabulary, and anything outside them gets rebuilt in your own tooling.

The bill that is not on the pricing page

EKS charges an hourly fee per cluster whatever is running inside it, and a cluster left on a version in extended support is billed at a higher hourly rate. That is the visible cost and it is the smaller one. What else production adds to the invoice is its own subject, and it is covered in moving EKS to production and the five bills nobody budgets.

The larger cost is the version treadmill, whose shape AWS documents precisely. The Kubernetes community releases a new minor version on average once every four months. Each version gets 14 months of standard support on EKS, then 12 months of extended support, so 26 months in total. If you are still on a version when extended support ends, the control plane is auto-upgraded for you, and AWS is blunt about the terms. It can happen at any time after the end date, you get no notification, and it covers the control plane only. Managed node groups and self-managed nodes stay exactly where they are, on the old version, under a new control plane.

You also cannot skip versions. Upgrades go one minor version at a time, so a cluster two versions behind is two upgrades with two rounds of testing. The documented sequence is to check upgrade readiness, upgrade the control plane, then the nodes, then in-cluster software such as the cluster autoscaler, then the EKS add-ons, then the kubectl versions your engineers and CI runners use.

# where the cluster sits in the lifecycle, and when its clock runs out
aws eks describe-cluster-versions \
  --query 'clusterVersions[].[clusterVersion,status,endOfStandardSupportDate]' \
  --output table

# nodes must already match the control plane minor version before you move it
kubectl get nodes -o wide

aws eks update-cluster-version --name production --kubernetes-version 1.36

Two details catch people out. The upgrade needs up to five free IP addresses in the subnets you named at cluster creation, and the rollback window afterwards is 7 days, past which going back means building a new cluster and migrating onto it.

Add-ons, controllers, and the people who have to understand them

Every EKS cluster gets the VPC CNI, kube-proxy and CoreDNS installed as self-managed add-ons, and you can convert them to EKS add-ons so AWS packages and patches them. That is useful, and it is also just the floor. What teams install on top is a load balancer controller, a metrics server, an autoscaler such as Karpenter, a secrets integration, a logging agent, a monitoring stack, an ingress controller, and often a service mesh and a GitOps controller, each with its own compatibility matrix against the Kubernetes version you just moved to. Support differs per piece as well, since AWS fully supports its own add-ons, validates community add-ons only for version compatibility, and leaves Marketplace add-ons to a partner. That is a map of who you call at 3am for each moving part.

Then there is what arrives from upstream whether you asked for it or not. Kubernetes 1.35 stopped supporting cgroup v1 by default, so a kubelet on a cgroup v1 node refuses to start, and it is the last release supporting containerd 1.x. 1.36 permanently disabled the gitRepo volume type and tightened IP and CIDR validation, which is a manifest audit across everything you run. Upstream retired Ingress NGINX in March 2026, and AWS's own guidance says there is no drop-in replacement and that migrating will take engineering time.

EKS cluster insights help here and carry their own latency. AWS refreshes findings every 24 hours, but deprecated API usage is evaluated over a rolling 30 day window, so it can take up to 30 days after you fix the last offending caller before the insight clears. That window sets the pace of the upgrade.

None of this says Kubernetes is badly run. The work simply exists, recurs on a 14 month clock at best, and needs someone whose job description includes it.

Portability, and what survives contact with a real account

The portability argument for Kubernetes is that your manifests run anywhere. That is true in a narrow sense, and it erodes with every AWS-specific thing you wire in, most of which you wire in because it is the better option here.

EKS Pod Identity is the cleanest illustration. It maps an IAM role to a Kubernetes service account, it is simpler than IAM roles for service accounts because it drops the OIDC provider, and AWS states plainly that its components are only available on Amazon EKS. Not EKS Anywhere, not Outposts, and not a cluster you run yourself on EC2.

Underneath that sits the VPC CNI handing pods real VPC IP addresses and security groups. Your ingress is very likely an ALB provisioned by the AWS Load Balancer Controller from annotations that mean nothing anywhere else, storage is EBS or EFS through their CSI drivers, and autoscaling is Karpenter picking EC2 instance types. Move that cluster to another provider and the pod specs come with you, while identity, networking, ingress, storage and scaling all get rewritten.

None of that is a reason to avoid EKS. It is a reason to stop counting portability as free. If it is a stated requirement, budget for keeping it, which means abstractions you maintain and a second environment you actually deploy to. If it is an unstated hope, price EKS on what it does for you on AWS today.

Fargate changes the shape of the argument

Both services run on Fargate, which takes the question of who patches the nodes off the table. What is left over differs sharply.

ECS on Fargate is the smoothest path either service offers. There is no node layer at all, and Fargate Spot is available as a capacity provider for interruption-tolerant work, with a two-minute warning delivered as a SIGTERM and a stopTimeout you can set as high as 120 seconds to shut down cleanly. AWS does not substitute on-demand capacity when Spot is unavailable, so a service that must not sit waiting needs a mixed strategy.

EKS on Fargate is more constrained than most teams expect. Each pod gets its own VM boundary and kernel, which is genuine isolation, and the list of what does not work is long. DaemonSets are not supported, so every node-level agent becomes a sidecar in every pod. No privileged containers, no GPUs, no Arm, no Windows, no EBS volumes, and EFS only with static provisioning. Private subnets only, no instance metadata service, and no Fargate Spot on EKS at all. Upgrades do not disappear either, because after a control plane upgrade you still have to delete and redeploy Fargate pods to move their kubelet version.

EKS Auto Mode is the more interesting reply to this whole article. AWS takes over the node lifecycle on Bottlerocket-based images with SSH and SSM access disabled, enforces a maximum node lifetime of 21 days with automatic replacement, runs Karpenter-based autoscaling, and folds pod networking, cluster DNS, load balancing, block storage and the pod identity agent into managed capabilities rather than add-ons you version yourself. Node updates follow your pod disruption budgets once you have upgraded the control plane. It is charged on top of the EC2 instance price and removes a large share of the operational cost this article is about, so if you have already decided on Kubernetes, look hard at it before assembling that stack by hand.

How to decide

Set aside which service is better and answer four questions about your own situation.

Are you using the Kubernetes API as an extension point today, meaning CRDs, operators, admission webhooks, or scheduling a placement constraint cannot express? A yes settles it in favour of EKS and nothing below outweighs it.

Who owns the upgrade? Name the person. A 14 month window, one minor version per step, node groups that do not move themselves and an add-on stack with its own compatibility matrix add up to recurring work that needs a slot in the plan. Whether that sits with your team or with an outside AWS cloud management arrangement matters less than whether it sits with anyone at all.

What does portability actually mean for you? If there is a second target you already deploy to, the argument holds. If it is insurance against a migration nobody has scheduled, notice that Pod Identity, the VPC CNI, the load balancer controller and the CSI drivers are AWS-only, and the platform layer is what you would be rewriting anyway.

Where do you want the compute question answered? ECS on Fargate removes nodes entirely, and EKS Auto Mode removes most of the node and add-on work while keeping the Kubernetes API. Both beat a hand-built node group with a pile of Helm charts that somebody will inherit.

The decision also carries less weight than it looks, because container images do not change between the two and a later migration touches the deployment description and the networking wiring. Teams that start on ECS and outgrow it get a clear signal telling them when. Teams that start on EKS because it is the default rarely find out they were wrong, because the cost never appears as a line item. It appears as the week that went into the upgrade.

Or read how we handle it in AWS Cloud Management.