Libby Louis

Rambling thoughts from a curious engineer

ECS Fargate vs. Kubernetes: Picking the Right Container Orchestrator

If you’re running containers in production, you’ve probably had the “should we just use Kubernetes?” conversation. Maybe you’re already on ECS Fargate and wondering if you’re missing out. Maybe you’re on Kubernetes and envious of teams that don’t have a platform team. Either way, the choice is less about which is “better” and more about what you’re optimizing for.

Here’s how the two actually compare once you’re past the marketing pages.

What they are

ECS Fargate is AWS’s serverless container runtime. You define a task (one or more containers, CPU, memory, IAM roles, networking) and a service (desired count, load balancer, deployment strategy). AWS handles provisioning, patching, and scaling the underlying compute. There are no EC2 instances, no AMIs, no kubelet. You write a task definition, point it at an image, and deploy.

Kubernetes (whether self-managed on EC2, EKS, GKE, AKS, or bare metal) is a general-purpose container orchestration platform. You get pods, deployments, services, ingress, RBAC, custom resource definitions, operators, a plugin ecosystem, and a steep learning curve to match. EKS is AWS’s managed Kubernetes offering — AWS runs the control plane, you still manage node groups (or use Fargate as a pod execution environment under EKS, which is a hybrid of both worlds).

Where Fargate wins

Operational simplicity

With Fargate, there is no cluster infrastructure to manage. No node pools to right-size, no OS patching schedule, no kubelet version skew, no etcd backup strategy, no control plane upgrade runbook. Your unit of work is a task definition — a JSON document with your image, resource limits, secrets references, and log config. That’s it.

For a team of 1–10 engineers shipping a product, this matters enormously. The gap between “I pushed a Docker image” and “it’s running in production with TLS, logging, and autoscaling” is measured in a Terraform module, not months of platform engineering.

IAM integration is native

Every ECS task can have its own IAM task role and execution role. The task role is what your application code uses to talk to AWS services (S3, Secrets Manager, Bedrock, etc.). The execution role is what ECS uses to pull images and fetch secrets on your behalf. This is clean, well-documented, and works out of the box.

In Kubernetes, the equivalent is IRSA (IAM Roles for Service Accounts) on EKS or workload identity on GKE. It works, but it’s an additional layer of configuration — you’re mapping Kubernetes service accounts to IAM roles through OIDC federation. It’s not hard, but it’s one more thing to set up and debug when permissions don’t work.

Secrets Manager and Parameter Store are first-class

In an ECS task definition, you can reference a Secrets Manager ARN directly in the secrets block and ECS injects it as an environment variable at startup. No sidecar, no init container, no CSI driver.

Kubernetes has its own Secret objects, but they’re base64-encoded (not encrypted at rest by default), and if you want to use an external secrets store, you need something like External Secrets Operator or the Secrets Store CSI Driver. These work well once configured, but they’re additional moving parts.

Pricing is straightforward

Fargate bills per vCPU-second and per GB-second of memory. You pay for what your tasks use. There’s no control plane fee (unlike EKS, which charges for the cluster, even before you run anything). For small-to-medium workloads, Fargate is often cheaper because you’re not paying for idle node capacity or a control plane.

Deployment is simple

ECS services support rolling deployments and circuit breaker rollback natively. You update the task definition, ECS drains old tasks and starts new ones. For most web services, this is all you need. No Helm, no ArgoCD, no GitOps controller — just aws ecs update-service or a Terraform apply.

Where Kubernetes wins

Portability

This is Kubernetes’ strongest argument. A deployment manifest that works on EKS also works on GKE, AKS, or a Raspberry Pi cluster in your closet. If you genuinely need multi-cloud or hybrid-cloud, or if you want to avoid deep AWS lock-in, Kubernetes gives you an abstraction layer that ECS simply doesn’t.

ECS is AWS-only. Your task definitions, service discovery, and deployment tooling are all AWS-specific. If your business strategy includes running on a customer’s Azure subscription or in an on-prem data center, ECS is a non-starter.

Ecosystem and extensibility

The Kubernetes ecosystem is vast. Service meshes (Istio, Linkerd), progressive delivery (Argo Rollouts, Flagger), policy engines (OPA/Gatekeeper, Kyverno), custom operators for databases and message queues, GitOps controllers (ArgoCD, Flux) — the list goes on.

ECS has none of this. If you want canary deployments, you’re bolting on CodeDeploy or building it yourself. If you want a service mesh, you’re using App Mesh (which AWS has essentially abandoned in favor of VPC Lattice). If you want custom controllers that react to your application’s domain events, you’re writing Lambda functions and EventBridge rules, not Kubernetes operators.

Scheduling sophistication

Kubernetes has a rich scheduling model: node affinity, pod anti-affinity, taints and tolerations, topology spread constraints, priority classes, preemption. If you need to co-locate certain workloads, spread them across failure domains, or implement sophisticated bin-packing, Kubernetes gives you the knobs.

Fargate’s scheduling is opaque. AWS places your task somewhere with the requested CPU and memory. You can spread across availability zones via service configuration, but you can’t express “put this task on the same host as that task” or “never schedule this next to that.” For most web services this doesn’t matter, but for stateful workloads, ML training, or latency-sensitive co-location it can be a real limitation.

Local development and testing

You can run a full Kubernetes cluster locally with minikube, kind, or k3d. Your CI can spin up a cluster, deploy your manifests, run integration tests, and tear it down. The inner loop of “write code, deploy to cluster, test” is well-tooled.

ECS doesn’t have an equivalent local runtime. You can run Docker containers locally, but you can’t test ECS service discovery, task IAM roles, or Secrets Manager injection without deploying to AWS. Tools like LocalStack partially bridge this gap, but it’s not the same.

Community and hiring

Kubernetes is the industry default for container orchestration. More engineers know it, more blog posts cover it, more conference talks discuss it. If you’re hiring platform engineers or SREs, “we use Kubernetes” is a simpler story than “we use ECS Fargate” — not because ECS is worse, but because Kubernetes is the lingua franca.

This cuts both ways: plenty of teams adopted Kubernetes because it was the default, not because they needed it, and now they maintain complexity they didn’t have to take on.

The honest tradeoff

The decision usually comes down to this:

Choose Fargate when:

  • You’re a small-to-medium team and don’t want to (or can’t) invest in platform engineering.
  • You’re AWS-native and not planning to leave.
  • Your workloads are stateless web services, APIs, and background workers.
  • You want the shortest path from a Docker image to a production service.
  • You value simplicity and are willing to accept less flexibility.

Choose Kubernetes when:

  • You need multi-cloud, hybrid-cloud, or on-prem deployment.
  • You have (or plan to hire) a platform team that can maintain the cluster.
  • You need the ecosystem: service meshes, progressive delivery, custom operators.
  • Your workloads have complex scheduling, co-location, or GPU requirements.
  • Portability and vendor independence are strategic priorities.

The uncomfortable middle ground: EKS with Fargate as the pod execution environment. This gives you the Kubernetes API and ecosystem without managing nodes, but it comes with limitations — no DaemonSets, no privileged containers, no persistent volumes (EBS) without workarounds, and cold start latency on pod scheduling. It’s a reasonable compromise for some teams, but it’s not a free lunch.

What nobody tells you

Kubernetes complexity is front-loaded but never-ending. Getting a cluster running is the easy part. Keeping it healthy — upgrades, certificate rotation, etcd maintenance, node draining, CNI plugin updates, admission controller debugging — is a permanent tax. If your team is four engineers building a product, that tax can consume 20–30% of your infrastructure time.

Fargate’s limitations tend to surface later. It’s great until you need something it can’t do: a sidecar pattern that requires shared process namespaces, a DaemonSet for log collection, a GPU workload, or a deployment strategy more nuanced than rolling update. At that point, you either work around it or migrate.

The migration in either direction is real work but not catastrophic. Your containers are Docker images either way. The orchestration layer (task definitions vs. manifests) is different, but the application code doesn’t change. The pain is in the surrounding infrastructure: networking, secrets management, IAM, CI/CD pipelines, and monitoring integrations.

Bottom line

ECS Fargate is the right default for most AWS-native teams building web applications. It’s dramatically simpler to operate, and for the majority of workloads, you’ll never hit its ceiling. Kubernetes is the right choice when you’re building a platform, need portability, or need the ecosystem. The wrong choice is picking Kubernetes because it’s the industry standard and then spending your first six months building the platform instead of the product.