AWS Elastic Beanstalk vs ECS vs EKS — What, When, and How
A comparison of the three main AWS services for running apps and containers — Elastic Beanstalk, ECS, and EKS. We cover, in order: ① how they differ, ② where they land on the PaaS/IaaS/CaaS spectrum, and ③ how a user actually deploys a container (Pod).
[01] Comparison at a glance
| Aspect | Elastic Beanstalk | ECS (Elastic Container Service) | EKS (Elastic Kubernetes Service) |
|---|---|---|---|
| What it is | Application deployment platform | AWS-native container orchestrator | Managed Kubernetes |
| Standard/engine | AWS proprietary (uses EC2/ECS internally) | AWS proprietary (task/service model) | Standard Kubernetes (Pod/Deployment) |
| Abstraction level | Highest (just push code) | Medium | Lowest (most control) |
| Your responsibility | Code/config | Task definitions + (on EC2) nodes | Manifests + (on self-managed) worker nodes |
| Portability | Low (AWS-locked) | Low (AWS-locked) | High (same K8s API anywhere) |
| Learning curve | Low | Medium | High |
| Typical use | Simple/traditional web apps, fast deploys | Container workloads that stay inside AWS | Multi-cloud, complex microservices, K8s ecosystem (Helm, etc.) |
One-line summary — “Want it up fast → Beanstalk”, “Just run containers inside AWS → ECS”, “Need the standard Kubernetes ecosystem → EKS”.
[02] A closer look at each
2-1. Elastic Beanstalk — “just throw me the code”
Upload your code (or a Docker image) and AWS automatically provisions the EC2 instances, load balancer, auto scaling group, and health checks. You barely touch the infrastructure.
- Supported runtimes: Java, .NET, Node.js, Python, PHP, Go, Ruby + Docker
- The infrastructure is hidden but not fully locked away — you can still tweak the generated EC2/security groups if needed.
- It is not a container orchestration tool. It’s a platform that automates “application deployment.”
If you need fine-grained multi-container handling, detailed scheduling, a service mesh, or ecosystem tools like Helm, Beanstalk hits its ceiling quickly. That’s when you move to ECS/EKS.
2-2. ECS — “AWS’s own container orchestrator”
Not Kubernetes — this is AWS’s own orchestrator. It works around Task Definition (container spec) and Service (maintain a desired count). It has two launch types.
| Launch Type | Description | Character |
|---|---|---|
| EC2 | You manage the EC2 nodes yourself, containers run on top | Closer to IaaS |
| Fargate | Serverless — run containers with no node management | Closer to PaaS (serverless) |
- Pros: seamless integration with AWS services (IAM, ALB, CloudWatch, …), no need to know Kubernetes, no control-plane cost.
- Cons: AWS lock-in (not portable to other clouds), can’t use the Kubernetes ecosystem (Helm/operators/etc.).
2-3. EKS — “managed Kubernetes”
AWS runs standard Kubernetes for you. AWS manages the control plane (API server, etcd, …) and you focus on the worker nodes and your workloads (Pods).
| Node operating mode | Description |
|---|---|
| Managed node groups | AWS manages the EC2 worker node lifecycle |
| Self-managed nodes | You operate the worker nodes (maximum control) |
| Fargate | Run Pods serverless, with no nodes |
- Pros: standard K8s API → operate identically on-prem/other clouds (portability), access to the huge ecosystem (Helm, operators, Istio, ArgoCD…).
- Cons: steep learning curve, hourly control-plane cost, high operational complexity.
[03] PaaS or IaaS? — the precise classification
People often ask “Beanstalk=PaaS, ECS/EKS=?”, but precisely speaking all three live on the CaaS (Container as a Service) spectrum between IaaS and PaaS. The real question is “how much does the user remain responsible for (the abstraction level)?”
1
2
3
4
More managed by you (IaaS) ←──────────────────────────→ Less (PaaS/serverless)
EC2 ECS(EC2) / EKS(self-nodes) ECS(Fargate) / EKS(Fargate) Beanstalk Lambda
[IaaS] [CaaS, IaaS side] [CaaS, serverless/PaaS side] [near PaaS] [FaaS]
| Service | Classification | One-liner |
|---|---|---|
| Elastic Beanstalk | Closest to PaaS | Give it code, the platform owns the entire runtime |
| ECS / EKS (EC2 / self-nodes) | CaaS (IaaS side) | You manage nodes → infra responsibility remains, IaaS-like |
| ECS / EKS (Fargate) | CaaS (PaaS/serverless side) | Node management disappears, closer to PaaS |
In other words, ECS and EKS slide between IaaS and PaaS depending on the launch type. With Fargate you stop seeing servers (nodes), so it leans toward PaaS/serverless; with EC2/self-managed nodes the infra responsibility grows, leaning toward IaaS. That’s exactly why flatly declaring “EKS is PaaS” or “EKS is IaaS” is wrong.
[04] How a user deploys a container (Pod) — the EKS (Kubernetes) flow
In Kubernetes the smallest deployable unit is the Pod. But you rarely create Pods directly — you manage them through a Deployment (a declaration of the desired state). The full flow is these 7 steps.
1
2
3
4
5
6
7
8
9
10
[1] code [2] build image [3] push to registry (ECR)
app.py ──▶ docker build ──▶ <acct>.dkr.ecr...amazonaws.com/app:v1
│
▼
[4] write manifest [5] kubectl apply [6] scheduler places Pods on nodes
deployment.yaml ──▶ submit to API server ─▶ Node A: [Pod] [Pod]
Node B: [Pod]
│
▼
[7] expose via Service/Ingress + HPA autoscaling + rolling updates
4-1. Build the image → push to the registry
1
2
3
4
5
6
7
8
# 1) Build the container image
docker build -t my-app:v1 .
# 2) Log in to ECR + tag + push
aws ecr get-login-password --region ap-northeast-2 \
| docker login --username AWS --password-stdin <acct>.dkr.ecr.ap-northeast-2.amazonaws.com
docker tag my-app:v1 <acct>.dkr.ecr.ap-northeast-2.amazonaws.com/my-app:v1
docker push <acct>.dkr.ecr.ap-northeast-2.amazonaws.com/my-app:v1
4-2. Write the manifest (Deployment)
Instead of creating Pods directly, you declare with a Deployment “how many Pods, from which image, to keep running.”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# deployment.yaml — declare the desired state
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3 # always keep 3 Pods
selector:
matchLabels:
app: my-app
template: # ↓ this is the Pod template
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: <acct>.dkr.ecr.ap-northeast-2.amazonaws.com/my-app:v1
ports:
- containerPort: 8080
4-3. Apply to the cluster → scheduling
1
2
3
4
5
# Configure cluster access (EKS)
aws eks update-kubeconfig --name my-cluster --region ap-northeast-2
# Apply the manifest — submit the "desired state" to the API server
kubectl apply -f deployment.yaml
From here Kubernetes handles the rest on its own.
- The API server receives the request and stores “3 Pods needed” in etcd.
- A controller requests Pod creation to go from 0 → 3.
- The scheduler places each Pod on a node with spare capacity.
- Each node’s kubelet pulls the image via the container runtime and starts the containers.
1
2
3
# Check deployment status
kubectl get pods -o wide # which node each Pod is Running on
kubectl get deployment my-app # confirm 3/3 READY
4-4. Expose externally (Service / Ingress)
Pod IPs change constantly, so you put a Service (or Ingress) in front as a stable entry point.
1
2
3
4
5
6
7
8
9
10
11
12
# service.yaml — give the set of Pods a stable entry point + load balancer
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
type: LoadBalancer # auto-creates an AWS LB (EKS)
selector:
app: my-app # routes traffic to Pods labeled app=my-app
ports:
- port: 80
targetPort: 8080
1
2
kubectl apply -f service.yaml
kubectl get svc my-app # check the EXTERNAL-IP (the LB address)
4-5. Scaling & zero-downtime updates
1
2
3
4
5
6
7
8
9
10
# Manual scale: 3 Pods → 5
kubectl scale deployment my-app --replicas=5
# Autoscale (HPA): adjust between 2–10 based on 50% CPU
kubectl autoscale deployment my-app --cpu-percent=50 --min=2 --max=10
# Rolling update to a new version (zero downtime): swap the image, it rolls gradually
kubectl set image deployment/my-app my-app=<acct>.dkr.ecr.ap-northeast-2.amazonaws.com/my-app:v2
kubectl rollout status deployment/my-app # track progress
kubectl rollout undo deployment/my-app # roll back if something breaks
The key is that it’s declarative. You don’t say “build it this way” — you declare “I want it to be in this state” (replicas: 3, image: v2), and Kubernetes converges the current state toward that target. ECS is conceptually similar with tasks/services, and Beanstalk wraps this whole thing one more level so a single eb deploy does it all.
[05] So which should you pick?
| Situation | Recommendation |
|---|---|
| You don’t know Kubernetes/containers well and want a traditional web app up fast | Elastic Beanstalk |
| You run containers only inside AWS and want to avoid K8s operational burden | ECS (+ Fargate if you hate managing nodes) |
| You need multi-cloud, portability, and the standard K8s ecosystem (Helm/operators) | EKS |
| You want to never see a node (server) at all | Fargate (works for both ECS/EKS) |
“Everyone else uses EKS, so we should too” is a common trap. Kubernetes brings operational complexity and control-plane cost. If you’re running a handful of containers in a single AWS environment, ECS (Fargate) is often cheaper and simpler.
[06] Key takeaways
| # | Takeaway |
|---|---|
| 1 | Beanstalk = app deployment platform, ECS = AWS-native orchestrator, EKS = managed standard Kubernetes |
| 2 | Strictly, all three are CaaS between IaaS and PaaS — Beanstalk is closest to PaaS |
| 3 | ECS/EKS shift position by launch type — Fargate leans PaaS/serverless, EC2/self-nodes lean IaaS |
| 4 | EKS portability ⬆ (standard K8s API); ECS/Beanstalk are more AWS-locked |
| 5 | Pod deployment in 7 steps: build → push to ECR → manifest → kubectl apply → scheduler places → expose via Service → scale/rolling update
|
| 6 | The essence of Kubernetes is declarative — declare the “desired state” and the system converges to it |
The one-line decision rule — fast deploys → Beanstalk, AWS-only containers → ECS, standard Kubernetes ecosystem → EKS.