If I set up a normal backend application on AWS today, I do not start with Kubernetes. I start with ECS on Fargate.
Not because Kubernetes is bad. I have worked with Kubernetes, in production too, and there are systems where I would use it exactly the same way again. But Kubernetes solves a class of problems that a normal application often does not have yet. And I do not choose architecture by what a system might theoretically need one day, but by what it actually needs today.
What is actually being compared
Strictly speaking, the question does not compare like with like. Amazon ECS and Kubernetes are orchestration systems. AWS Fargate, by contrast, is the compute layer containers run on, and it can be used under ECS as well as under Amazon EKS.
So when I say ECS Fargate, I mean the stack on the left; by Kubernetes in an AWS context, the one on the right:
ECS Fargate Kubernetes on AWS
Container Container
↓ ↓
Amazon ECS Amazon EKS
↓ ↓
AWS Fargate EC2, EKS Auto Mode or FargateThe real decision is therefore: does my application need the Kubernetes platform, or is the much narrower abstraction of ECS enough? For many applications my answer is: ECS is enough.
What I mean by a normal application
So the statement does not become arbitrary, I narrow down "normal". I mean a SaaS backend, an API, a PHP monolith, a handful of Go services or an internal business application, shaped roughly like this:
- a few HTTP services and background jobs,
- a relational database, Redis and a queue such as SQS,
- a load balancer in front,
- automated deployments and horizontal scaling,
- logs, metrics and tracing.
Maybe three containers, maybe ten, maybe twenty one day. These are serious production systems. But they are not yet a container platform. The difference matters, because "we run several containers" turns into "we need Kubernetes" surprisingly often without anyone noticing. The one does not follow from the other.
What ECS Fargate already brings
With ECS on Fargate I essentially describe which container I want to run, how much CPU and memory it gets, what its network looks like, which IAM permissions it has and how many instances should run. I do not provision the servers underneath or manage them as cluster capacity.
A typical web service then looks like this, extended depending on the application with SQS, ElastiCache, Secrets Manager, ECR, Route 53 and CloudWatch or OpenTelemetry:
Application Load Balancer
↓
ECS service
↓
Fargate tasks
↓
RDSThat is not a toy architecture. The service itself answers the everyday questions:
- The service needs four instances? Then four tasks run.
- Traffic grows? Then the service scales via auto scaling on a metric.
- A new version is coming? ECS starts new tasks, waits for the health check and takes the old ones out of traffic. If the deployment fails, the deployment circuit breaker rolls back to the last working version.
- A task dies? The service replaces it.
- I need to get into a running container? ECS Exec does that, without SSH and without an open port.
With that you can build highly available, horizontally scaling, automatically deployed applications. Above all: I run the application, not a platform for the application on top. For me, that is the deciding point.
What that looks like for an existing PHP monolith, from the image to the health check, is covered in A PHP monolith on ECS Fargate.
What Kubernetes adds, and what it costs
Kubernetes can do a great deal more. Deployments describe a desired state that the platform brings about declaratively, and around that core sits a large ecosystem for networking, storage, autoscaling, policies, operators, service meshes and custom controllers.
Amazon EKS takes an important part of the operations off your hands: AWS runs and scales the control plane and replaces unhealthy instances there. That is worth a lot. But a managed control plane does not turn Kubernetes into ECS. The system model remains, and someone on the team has to understand it:
Cluster, Namespaces, Deployments, Pods, Services, Ingress,
ConfigMaps, Secrets, ServiceAccounts, RBAC, Network Policies,
Autoscaler, Storage Classes, CRDs, Controller, ...None of these building blocks is bad. The only question is: which problem of my application does it solve? If I have no concrete answer, I do not want to own it.
Because complexity is not free just because it is made of YAML. One more manifest, one more Helm chart, one more controller all look cheap at first. The price comes later: at the upgrade, in the incident, during onboarding, with the next security vulnerability. When a certificate is not renewed. When a controller stops working with the new Kubernetes version. When someone has to understand why the pod is running but the service gets no traffic, or why a successful deployment is isolated by a network policy.
The upgrade is not a one-off either; it is a rhythm. On EKS a Kubernetes version stays in standard support for around 14 months, after which extended support costs considerably more. Running EKS therefore means upgrading the cluster roughly once a year, together with every add-on running on it. ECS has no such rhythm, because there is no control plane that I version.
Technology does not become free because AWS manages part of it. Every platform feature I introduce is knowledge the team has to have.
Four arguments that are not enough on their own
These are the four reasons for Kubernetes I hear most often. Each contains something true; none carries the decision alone.
"Kubernetes is more flexible." True. But every additional possibility creates an additional decision. If you can run different ingress controllers, someone has to pick one. If you can install custom controllers, someone has to update them. If you can use a hundred extensions, someone has to decide which ten become a permanent part of the platform. With ECS the solution space is smaller. That can be a drawback; for a normal application I usually find it an advantage. I do not want the most flexible platform, but the smallest one that solves my problem reliably.
"ECS means vendor lock-in." Technically correct: task definitions, task roles and ECS services are AWS-specific. EKS, on the other hand, implements upstream Kubernetes and is conformant, so a deployment can in principle run elsewhere. The question is how much portability is left in the real application. If it uses RDS, S3, SQS, SNS, EventBridge, IAM, Secrets Manager, CloudFront and DynamoDB, a Kubernetes deployment does not make it cloud-independent. My compute has become more portable; my system has not. If multi-cloud is a concrete business requirement, the calculation changes. "Maybe we will leave AWS one day" is not enough for me.
"We want to grow fast." Scaling is not a single problem. More requests need more instances; ECS does that. A growing queue needs more workers; ECS does that too. A database at its limit or a slow external service: there Kubernetes does not help at all. In classic backends the first bottleneck is almost never orchestration, but database access, the data model, caching, synchronous calls, locks, hot partitions, large payloads or missing backpressure. So my counter-question is: which component does not scale today?
"We have microservices." Five services do not need a cluster just because they are called microservices, and ten do not automatically either. Under ECS every service gets its own resources, its own IAM permissions, its own deployment and its own scaling, and Service Connect lets them find each other. The right question is not "do we have microservices?" but: which requirement of our service landscape can ECS no longer represent sensibly?
Whether it needs to be several services at all is a question that comes first: From monolith to microservices.
When Kubernetes earns its complexity
The point at which my decision tips is not a number of requests and not a number of containers. It is the moment I no longer mainly run individual applications, but a shared platform for many applications and teams.
With thirty or more services, several teams with their own release cycles, different kinds of workloads, shared standards, central policies and people whose job is the platform, the calculation changes. Then a uniform deployment mechanism, a shared policy layer, standardized workload definitions and self-service for the teams pay off. Then Kubernetes no longer adds complexity; it organizes the complexity that is already there. With five services the platform may create more complexity than it removes. With fifty it can do the opposite.
On top of that come requirements where the larger platform shows its value quickly: special scheduling rules, custom operators, demanding stateful workloads, far-reaching network and policy requirements, standardization across several infrastructure environments, or tools that presuppose Kubernetes.
And finally the organization. If a platform team in the company already runs Kubernetes in production, with monitoring, deployment, security policies and organized upgrades, then Kubernetes is not new there. A separate ECS path for my application may then be the more complicated one, and I would not say "ECS is simpler". The existing organization is part of the system. Conversely, I never introduce Kubernetes because two developers would like to learn it. That is a nice side effect, not an architectural reason.
That is why I do not say ECS is better than Kubernetes. I say: Kubernetes has to solve a problem that ECS does not solve well enough. If that problem exists, Kubernetes can be the better decision. If it does not, I am buying platform complexity for nothing in return.
For a grown system that has lived on one server so far, the same question comes with different premises: Does the legacy application belong on Kubernetes?
Cost: more than the price of a vCPU
A comparison quickly ends up in tables of vCPU and RAM prices. That is necessary, but not enough for an architectural decision. I look at at least three blocks:
compute cost
+ platform cost
+ operating costAt a constantly high load, Fargate can cost more than well-utilized EC2 capacity. In return I buy less infrastructure work, and for interruptible workers there is Fargate Spot. Kubernetes may let you pack workloads more densely and optimize compute further. In return you add a per-cluster fee for the control plane, the yearly upgrade rhythm and a more powerful platform that somebody has to run.
So the figure I care about is not the price of a vCPU, but: what does it cost us to run this application reliably for twelve months? That includes developer time, updates, on-call, debugging and knowledge.
Where the money actually disappears in grown AWS setups is covered in Bad architecture sends no alerts.
An example, and the same company three years later
An application on AWS with a public API, a payment service, a reporting service and a worker, plus PostgreSQL, Redis and SQS. All services are stateless, the team has six developers and no platform team. The application has to be highly available and scale automatically.
I see no reason for Kubernetes here. My first architecture would be:
ALB
|
+--------+--------+
| |
API service Payment service Reporting service
ECS/Fargate ECS/Fargate ECS/Fargate
SQS
|
Worker
ECS/Fargate
RDS + ElastiCache (Multi-AZ)Deployment via CI/CD, infrastructure via Terraform, observability via OpenTelemetry, auto scaling on sensible metrics, Multi-AZ where the state lives. That is a complete production environment. What would Kubernetes additionally solve here? If the only answer is "then we would be on Kubernetes", I do not introduce it.
Now the same company three years later. Six developers have become sixty, four workloads have become eighty, eight teams deploy independently. The platform is supposed to enforce shared rules for networking, security, observability, secrets, deployments and resources. A platform team exists, some workloads have special scheduling requirements, several central tools presuppose Kubernetes.
Now the same decision looks completely different. A shared platform on which every team works by the same rules replaces dozens of individual solutions. Now Kubernetes earns its complexity. The line is not the number of requests and not the number of certifications on the team, but whether I run an application or a platform for many.
When Fargate is not enough
Fargate has limits, and they are concrete: no GPUs, no privileged containers, no daemons on the host, a ceiling for CPU and memory per task. Hitting one of them does not automatically mean you need Kubernetes.
The next step is often ECS on EC2: the same service model, the same task definitions, but your own instances with a GPU, more memory or an agent that has to run on every host. The platform question only arises once the requirements go beyond what ECS can represent at all.
The decision as a table
| Question | ECS on Fargate | Kubernetes / EKS |
|---|---|---|
| Few to a moderate number of services | usually my choice | often more platform than needed |
| Small development team | a very good fit | mind the knowledge overhead |
| No platform team | clear advantage | a strong counter-argument for me |
| Managing servers yourself | not needed | also greatly reduced, depending on the EKS model |
| Version upgrades | no rhythm of their own | roughly yearly, including add-ons |
| AWS integration | very direct | also very good |
| Portability of the orchestrator | AWS-specific | much stronger |
| Platform ecosystem | smaller | very large |
| Custom controllers and operators | no | core strength |
| Complex scheduling requirements | limited | stronger |
| Many teams on a shared platform | possible | this is where Kubernetes gets interesting |
| Fast start for a normal application | my favourite | extra platform decisions |
| Operational surface | smaller | larger |
| Organization already runs Kubernetes | possibly an unnecessary special path | then often the better choice |
But I would not let this table decide on its own either. The most important question is not in it.
The question I start with
When someone says "we should use Kubernetes", I do not ask which distribution. I ask: which concrete problem of our application does Kubernetes solve that we cannot sensibly solve with ECS Fargate?
There are very good answers to that. "We run 120 services with twelve teams." "Our internal platform model is based on it." "We need operators for a central part of our infrastructure." "We genuinely run the same workloads in several Kubernetes environments." All good. "It is the industry standard" is not enough for me. "It makes us scalable" is not either. And "maybe we will need it later" least of all.
This matters especially in modernization. If an application runs on a single virtual machine today, I do not try to move it straight onto Kubernetes. The sensible path is often:
1 server
↓
reproducible container
↓
ECS Fargate
↓
several tasks
↓
automated deployments
↓
auto scalingAnd maybe the journey ends there. That is not an interim state that has to be done "properly" one day; it can be the final architecture. If the organization does need a Kubernetes platform later, a cleanly containerized, stateless service is much easier to move there than the original legacy system. So I lose little. I only postpone a decision until I know enough to make it sensibly.
What an application has to get rid of first so that it can run more than once is covered in Sessions, uploads, state.
I choose Kubernetes when the requirements have changed: many teams, many different workloads, a real platform team, an ecosystem that presupposes Kubernetes, special scheduling or policy requirements, or a genuinely needed uniform platform across several environments. Then the platform pays back its complexity. Not before.
So my default is not the most powerful platform I could use, but the smallest one that solves the problem reliably.
If you are facing this decision right now, the approach is described on the page for AWS migration.

