EC2, ASG, EKS, and Fargate: A Complete Guide to Choosing Your AWS Compute Architecture
Choosing a compute service on AWS isn’t just about picking “the newest” or “the most popular.” Each service was born from a different need, and choosing wrong can mean over-engineering a simple system or under-engineering a critical one. The four services that most often cause confusion — EC2, Auto Scaling Groups, EKS, and Fargate — each have very different philosophies, trade-offs, and sweet spots. This article dissects all four in depth so you can make architecture decisions with confidence.
The Big Picture — Four Levels of Abstraction
Before diving into the details of each service, it’s important to understand that these four services aren’t equivalent choices. They sit at different abstraction levels, and each trades control for operational ease.
%%{init: {'flowchart': {'padding': 20}} }%%
graph TB
subgraph "AWS Compute Abstraction Levels"
direction TB
A["EC2<br/>Direct Virtual Machine<br/>Full control, manage everything"]
B["Auto Scaling Group<br/>EC2 + Autoscaling Controller<br/>Automatic horizontal scaling"]
C["EKS<br/>Managed Kubernetes<br/>Industry-standard container orchestration"]
D["Fargate<br/>Serverless Container<br/>No server management at all"]
end
A -->|"+ autoscaling"| B
B -->|"+ container orchestration"| C
C -->|"- node management"| D
style A fill:#fef3c7,stroke:#d97706,color:#000
style B fill:#fde68a,stroke:#d97706,color:#000
style C fill:#bfdbfe,stroke:#2563eb,color:#000
style D fill:#bbf7d0,stroke:#16a34a,color:#000The basic principle is simple: the higher the abstraction level, the smaller the operational burden — but the greater the per-unit cost and the more limited the control you have.
Amazon EC2 — The Classic Virtual Machine
EC2 is the foundation of compute on AWS. This service gives you a virtual machine with specs you choose yourself — CPU, RAM, disk, OS — then you’re free to use it like a physical server. SSH directly, install anything, configure however you like.
Why EC2 Is Still Relevant
In the era of containers and serverless, EC2 feels “old-fashioned.” But precisely because it’s the lowest level, EC2 becomes the right choice when you need full control. Legacy applications that can’t be containerized, workloads needing direct hardware access (GPU computing, high-performance networking), or systems depending on specific OS configurations — all of these fit better on EC2.
Characteristics and Trade-offs
| Strengths | Weaknesses |
|---|---|
| ✅ Full control over OS and configuration | ❌ Manual scaling without ASG |
| ✅ Good for legacy applications and monoliths | ❌ Full responsibility: patching, security, availability |
| ✅ Most flexible — install anything | ❌ Single point of failure if only one instance |
| ✅ Easy to understand for engineers at any level | ❌ Slower provisioning than containers |
| ✅ Per-second billing — efficient for stable workloads | ❌ Not automatically high-availability |
When EC2 Is the Right Choice
EC2 suits these scenarios: a monolith that hasn’t been refactored, stateful workloads storing data locally, systems needing kernel modules or special drivers, and experiments or learning environments where simplicity matters more than scalability.
EC2 Anti-Patterns
The most common mistake with EC2 is running a single instance for production without redundancy. One instance means one point of failure — if the instance dies from hardware failure (which in the cloud isn’t a question of “if” but “when”), the entire system goes down with it.
graph LR
subgraph "❌ Anti-Pattern: Single EC2"
U1["User"] --> EC1["EC2<br/>(single instance)"] --> DB1["Database"]
end
style EC1 fill:#fecaca,stroke:#dc2626,color:#000
style U1 fill:#f5f5f4,stroke:#78716c,color:#000
style DB1 fill:#e0e7ff,stroke:#4f46e5,color:#000graph LR
subgraph "✅ Correct: Multi-AZ + Load Balancer"
U2["User"] --> ALB["ALB"] --> EC2a["EC2-a<br/>AZ-1"]
ALB --> EC2b["EC2-b<br/>AZ-2"]
EC2a --> DB2["Database"]
EC2b --> DB2
end
style ALB fill:#bbf7d0,stroke:#16a34a,color:#000
style EC2a fill:#bfdbfe,stroke:#2563eb,color:#000
style EC2b fill:#bfdbfe,stroke:#2563eb,color:#000
style U2 fill:#f5f5f4,stroke:#78716c,color:#000
style DB2 fill:#e0e7ff,stroke:#4f46e5,color:#000EC2 Best Practices
- Use an IAM Role attached to the instance, not access keys stored on disk.
- Store configuration in SSM Parameter Store or Secrets Manager, not hardcoded in the AMI.
- Create a custom AMI for deployment consistency — every new instance must be identical.
- Enable the CloudWatch agent for CPU, memory, and disk monitoring from day one.
Auto Scaling Group — EC2 That Can Breathe
ASG isn’t a new compute service. ASG is a controller managing a group of EC2 instances automatically — adding instances when traffic rises, reducing when quiet, and replacing dead instances. If EC2 is a car, ASG is its cruise control system.
How ASG Works
ASG works based on three parameters: minimum (the lowest number of instances always alive), desired (the currently desired instance count), and maximum (the scaling upper bound). Scaling policies determine when desired changes — based on CPU utilization, request counts, custom metrics, or schedules.
graph LR
CW["CloudWatch Alarm<br/>'CPU > 70% for 5 minutes'"]
ASG["Auto Scaling Group<br/>min: 2 | desired: 2 | max: 8"]
LT["Launch Template<br/>t3.medium, AMI-xxx"]
CW -->|"trigger scale-out"| ASG
ASG -->|"launch new instance from"| LT
ASG --> EC2a["EC2 Instance 1<br/>AZ-1"]
ASG --> EC2b["EC2 Instance 2<br/>AZ-2"]
ASG -.->|"new instance"| EC2c["EC2 Instance 3<br/>AZ-1"]
ALB["Application<br/>Load Balancer"] --> EC2a
ALB --> EC2b
ALB -.-> EC2c
style CW fill:#fef3c7,stroke:#d97706,color:#000
style ASG fill:#bfdbfe,stroke:#2563eb,color:#000
style LT fill:#e0e7ff,stroke:#4f46e5,color:#000Characteristics and Trade-offs
| Strengths | Weaknesses |
|---|---|
| ✅ Automatic high availability — dead instances replaced immediately | ❌ Still must manage the OS and patching |
| ✅ Metric-based horizontal scaling | ❌ Relatively slow scaling (1-5 minutes per new instance) |
| ✅ Native ALB/NLB integration | ❌ Not suitable for very sudden burst traffic |
| ✅ Multi-AZ for fault tolerance | ❌ Applications must be stateless for scaling to work correctly |
| ✅ Mixed instance types (spot + on-demand) | ❌ Warm-up time makes scaling non-instant |
When ASG Is the Right Choice
ASG is the natural choice for traditional web backends, API servers, and EC2-based worker queues. This pattern is the most common in the industry because it balances simplicity with reliability. If your application isn’t containerized yet but needs high availability and autoscaling, ASG is the answer.
ASG Anti-Patterns
The biggest mistake with ASG is making the application stateful then expecting horizontal scaling to work. If every instance stores sessions in local memory, adding a new instance doesn’t help — users whose session lives on instance A will get errors when their next request lands on instance B.
graph LR
subgraph "❌ Anti-Pattern: Sessions in Local Memory"
R1["Request 1"] --> IA["Instance A<br/>session stored"]
R2["Request 2"] --> IB["Instance B<br/>no session ⚠️"]
end
style IA fill:#fef3c7,stroke:#d97706,color:#000
style IB fill:#fecaca,stroke:#dc2626,color:#000
style R1 fill:#f5f5f4,stroke:#78716c,color:#000
style R2 fill:#f5f5f4,stroke:#78716c,color:#000graph LR
subgraph "✅ Correct: Sessions in an External Store"
R3["Request 1"] --> IC["Instance A"] --> RD["Redis<br/>session store"]
R4["Request 2"] --> ID["Instance B"] --> RD
end
style IC fill:#bfdbfe,stroke:#2563eb,color:#000
style ID fill:#bfdbfe,stroke:#2563eb,color:#000
style RD fill:#bbf7d0,stroke:#16a34a,color:#000
style R3 fill:#f5f5f4,stroke:#78716c,color:#000
style R4 fill:#f5f5f4,stroke:#78716c,color:#000ASG Best Practices
- Always use a Launch Template (not the deprecated Launch Configuration) with tracked versions.
- Put an ALB or NLB in front of the ASG — don’t expose instances directly.
- Design the application stateless from the start. Store sessions, caches, and files in external services.
- Combine target tracking scaling (for example a 60% CPU target) with scheduled scaling for known traffic patterns.
- Use a mixed instances policy with spot instances to save up to 70% in costs.
Amazon EKS — Kubernetes Without Managing the Control Plane
EKS is managed Kubernetes on AWS. AWS manages the control plane (API server, etcd, scheduler), you manage the workload and — if using EC2 mode — the worker nodes. Kubernetes itself has become the de facto standard for container orchestration, and EKS makes adopting it on AWS far easier than building a cluster yourself.
Why Kubernetes, and Why EKS
Kubernetes solves the problems that arise when you have many containers: how to schedule them onto the right servers, how to ensure replica counts are correct, how to do rolling updates without downtime, and how to manage networking between services. EKS removes the heaviest burden — managing the control plane — so teams can focus on workloads.
graph TB
subgraph "EKS Cluster"
subgraph "AWS Managed"
CP["Control Plane<br/>API Server, etcd,<br/>Scheduler, Controller Manager"]
end
subgraph "You Manage (EC2 Mode)"
NG["Node Group"]
NG --> N1["Worker Node 1<br/>(EC2)"]
NG --> N2["Worker Node 2<br/>(EC2)"]
NG --> N3["Worker Node 3<br/>(EC2)"]
end
subgraph "Workload"
N1 --> P1["Pod A"]
N1 --> P2["Pod B"]
N2 --> P3["Pod C"]
N2 --> P4["Pod D"]
N3 --> P5["Pod E"]
end
end
CP ---|"orchestrate"| NG
style CP fill:#bbf7d0,stroke:#16a34a,color:#000
style NG fill:#bfdbfe,stroke:#2563eb,color:#000Characteristics and Trade-offs
| Strengths | Weaknesses |
|---|---|
| ✅ Kubernetes compliant — portable across clouds | ❌ Very high learning curve |
| ✅ Huge CNCF ecosystem (Helm, Istio, ArgoCD, etc.) | ❌ Control plane cost ($0.10/hr ≈ $73/month) on top of worker costs |
| ✅ Good for large-scale microservice architectures | ❌ Over-engineering for simple systems |
| ✅ Rolling updates, canary deployments, blue-green native | ❌ More complex debugging than direct EC2 |
| ✅ Lower vendor lock-in than ECS | ❌ Needs a team that understands Kubernetes |
When EKS Is the Right Choice
EKS is right when you run large-scale microservices (dozens to hundreds of services), have a team already experienced with Kubernetes, need portability across clouds (multi-cloud strategy), or are building an internal platform engineering layer. If you’re only running 2-3 services, EKS is most likely over-engineering.
EKS Anti-Patterns
The fatal mistake that often happens is adopting EKS without understanding Kubernetes. Teams that don’t understand the concepts of Pods, Services, Ingress, ConfigMaps, and RBAC will spend more time debugging infrastructure than building features.
graph LR
subgraph "❌ Anti-Pattern: EKS Without Readiness"
T1["3-person team"] --> EKS1["EKS"] --> S1["2 services"]
S1 --> H1["80% of time<br/>spent on infra"]
end
style T1 fill:#f5f5f4,stroke:#78716c,color:#000
style EKS1 fill:#fecaca,stroke:#dc2626,color:#000
style S1 fill:#fef3c7,stroke:#d97706,color:#000
style H1 fill:#fecaca,stroke:#dc2626,color:#000graph LR
subgraph "✅ Correct: EKS When Scale & Team Are Ready"
T2["10+ person team"] --> EKS2["EKS"] --> S2["20+ services"]
S2 --> H2["Infra handled<br/>by platform team"]
end
style T2 fill:#f5f5f4,stroke:#78716c,color:#000
style EKS2 fill:#bfdbfe,stroke:#2563eb,color:#000
style S2 fill:#bbf7d0,stroke:#16a34a,color:#000
style H2 fill:#bbf7d0,stroke:#16a34a,color:#000Another common mistake is running databases inside Kubernetes. Stateful workloads like databases require storage, backup, and failover management that’s far better handled by managed services (RDS, Aurora, ElastiCache).
EKS Best Practices
- Use namespaces for isolation between teams or environments, and apply ResourceQuotas to prevent one team from exhausting cluster resources.
- Implement HPA (Horizontal Pod Autoscaler) for CPU/memory-based scaling, and VPA (Vertical Pod Autoscaler) for right-sizing resource requests.
- Use IRSA (IAM Role for Service Account) so each Pod only has the AWS access it needs — don’t use node-level IAM roles.
- Require resource requests and limits in every Pod. Without them, the scheduler can’t make good placement decisions.
- Implement an observability stack from the start: Prometheus for metrics, Grafana for dashboards, Fluent Bit for log forwarding.
AWS Fargate — Containers Without Servers
Fargate shifts the paradigm: you define the container (image, CPU, memory), and AWS runs it. No instances to SSH into, no OS to patch, no capacity planning for nodes. Fargate can be used through two orchestrators: ECS (simpler) or EKS (if you’ve already invested in Kubernetes).
How Fargate Works
Every task or pod running on Fargate gets its own isolated micro-VM. You don’t share a kernel with other tenants. This makes Fargate more secure by default than containers running on shared EC2 nodes.
graph LR
subgraph "Without Fargate (EC2 Mode)"
EC2["EC2 Instance<br/>(you manage)"]
EC2 --> C1["Container A"]
EC2 --> C2["Container B"]
EC2 --> C3["Container C"]
end
subgraph "With Fargate"
F1["Fargate Task<br/>Container A<br/>(isolated micro-VM)"]
F2["Fargate Task<br/>Container B<br/>(isolated micro-VM)"]
F3["Fargate Task<br/>Container C<br/>(isolated micro-VM)"]
end
style EC2 fill:#fef3c7,stroke:#d97706,color:#000
style F1 fill:#bbf7d0,stroke:#16a34a,color:#000
style F2 fill:#bbf7d0,stroke:#16a34a,color:#000
style F3 fill:#bbf7d0,stroke:#16a34a,color:#000Characteristics and Trade-offs
| Strengths | Weaknesses |
|---|---|
| ✅ Zero server management — no OS, patching, or capacity planning | ❌ More expensive per vCPU/GB than direct EC2 |
| ✅ Per-task/pod security isolation (micro-VM) | ❌ Cold starts can reach 30-60 seconds for large images |
| ✅ Per-second billing based on resources used | ❌ Can’t SSH into the runtime environment |
| ✅ Scale-to-zero possible (with ECS) | ❌ Limited control — can’t tune the kernel or mount host volumes |
| ✅ Good for sporadic and event-driven workloads | ❌ Not suitable for GPU workloads or high-performance computing |
When Fargate Is the Right Choice
Fargate shines in three main scenarios. First, background jobs and event-driven workers — tasks triggered by events (SQS messages, S3 uploads, scheduled cron) that die after finishing. Second, microservices with low-to-medium traffic that don’t justify the cost of managing EC2 nodes. Third, small teams without a dedicated infra engineer who want to focus on writing code, not managing servers.
Fargate Anti-Patterns
A common mistake is running all workloads on Fargate without considering cost. For workloads running 24/7 with high utilization, EC2 (especially with Reserved Instances or Savings Plans) can be 3-5x cheaper.
graph LR
subgraph "❌ Anti-Pattern: Everything on Fargate"
SA1["Service A<br/>24/7, high CPU"] --> FG1["Fargate"] --> C1["Very high cost"]
SB1["Service B<br/>24/7, high CPU"] --> FG2["Fargate"] --> C2["Very high cost"]
end
style SA1 fill:#fef3c7,stroke:#d97706,color:#000
style SB1 fill:#fef3c7,stroke:#d97706,color:#000
style FG1 fill:#fecaca,stroke:#dc2626,color:#000
style FG2 fill:#fecaca,stroke:#dc2626,color:#000
style C1 fill:#fecaca,stroke:#dc2626,color:#000
style C2 fill:#fecaca,stroke:#dc2626,color:#000graph LR
subgraph "✅ Correct: Hybrid — Match the Workload"
SA2["Service A<br/>24/7, high CPU"] --> EC2["EC2 + ASG"] --> C3["Low cost"]
SB2["Service B<br/>event-driven"] --> FG3["Fargate"] --> C4["Minimal cost"]
end
style SA2 fill:#fef3c7,stroke:#d97706,color:#000
style SB2 fill:#e0e7ff,stroke:#4f46e5,color:#000
style EC2 fill:#bfdbfe,stroke:#2563eb,color:#000
style FG3 fill:#bbf7d0,stroke:#16a34a,color:#000
style C3 fill:#bbf7d0,stroke:#16a34a,color:#000
style C4 fill:#bbf7d0,stroke:#16a34a,color:#000Fargate Best Practices
- Set resource requests accurately. Fargate bills based on the vCPU and memory you request, not what you use. Over-provisioning directly wastes money.
- Use Fargate Spot for workloads tolerant of interruption (batch processing, data pipelines). Save up to 70%.
- Optimize container image size. Smaller images mean faster cold starts. Use multi-stage builds and alpine base images.
- Implement proper health checks so the orchestrator knows when a task needs restarting.
Direct Comparison
The following table summarizes the fundamental differences between the four services across various aspects.
| Aspect | EC2 | ASG | EKS | Fargate |
|---|---|---|---|---|
| Abstraction level | Low | Low–Medium | High | Very High |
| Autoscaling | Manual | Automatic horizontal | HPA + Cluster Autoscaler | Automatic per task/pod |
| Manage servers | Yes, fully | Yes, but ASG helps replace | Yes for worker nodes | Not at all |
| Provisioning time | 1-3 minutes | 1-5 minutes | Seconds (pod scheduling) | 30-60 seconds (cold start) |
| Security isolation | Shared tenancy (default) | Shared tenancy | Shared node | Micro-VM per task |
| Per-unit cost | Cheapest | Cheap | Medium + control plane | Most expensive per vCPU |
| Good for | Legacy, stateful, GPU | Traditional web/API | Large microservices | Event-driven, small teams |
| Learning curve | Low | Low | High | Low–Medium |
| Vendor lock-in | Medium | Medium | Low (K8s portable) | High |
Decision Guide — Choose the Right One
No need to memorize every detail. Use the following decision tree to guide your architecture decision.
graph TD
Start["Need to run an<br/>application on AWS"] --> Q1{"Is the app already<br/>containerized?"}
Q1 -->|"Not yet"| Q2{"Need autoscaling?"}
Q2 -->|"No"| EC2["✅ EC2<br/>Single or multi-instance"]
Q2 -->|"Yes"| ASG["✅ EC2 + ASG<br/>Traditional autoscaling"]
Q1 -->|"Yes"| Q3{"Does the team<br/>understand Kubernetes?"}
Q3 -->|"No"| Q4{"Need to manage<br/>servers yourself?"}
Q4 -->|"No"| FGECS["✅ Fargate + ECS<br/>Serverless container, simple"]
Q4 -->|"Yes"| ECSEC2["✅ ECS + EC2<br/>Simple container orchestration"]
Q3 -->|"Yes"| Q5{"How many<br/>services?"}
Q5 -->|"< 10"| FGECS2["✅ Fargate + ECS/EKS<br/>Kubernetes without node mgmt"]
Q5 -->|"> 10"| Q6{"Need full control<br/>over nodes?"}
Q6 -->|"Yes"| EKSEC2["✅ EKS + EC2 Node Group<br/>Full Kubernetes power"]
Q6 -->|"No"| EKSFG["✅ EKS + Fargate<br/>Serverless Kubernetes"]
style EC2 fill:#fef3c7,stroke:#d97706,color:#000
style ASG fill:#fde68a,stroke:#d97706,color:#000
style FGECS fill:#bbf7d0,stroke:#16a34a,color:#000
style ECSEC2 fill:#bfdbfe,stroke:#2563eb,color:#000
style FGECS2 fill:#bbf7d0,stroke:#16a34a,color:#000
style EKSEC2 fill:#bfdbfe,stroke:#2563eb,color:#000
style EKSFG fill:#d9f99d,stroke:#65a30d,color:#000Production Architecture per Type
Theory without real examples is hard to digest. Here are realistic production architectures for each approach.
Single EC2 — Minimum Viable Production
graph LR
User["User"] --> EC2["EC2 Instance<br/>t3.medium<br/>App + Nginx"]
EC2 --> RDS["RDS<br/>PostgreSQL"]
EC2 --> S3["S3<br/>Static Assets"]
style EC2 fill:#fef3c7,stroke:#d97706,color:#000Single point of failure. Suitable for early MVPs or internal tools. Don’t use it for significant public traffic.
EC2 + ASG — The Most Common Pattern
graph LR
User["User"] --> ALB["ALB"]
ALB --> EC2a["EC2<br/>AZ-1"]
ALB --> EC2b["EC2<br/>AZ-2"]
ALB --> EC2c["EC2<br/>AZ-1"]
EC2a --> RDS["RDS Multi-AZ"]
EC2b --> RDS
EC2c --> RDS
EC2a --> Redis["ElastiCache<br/>Redis"]
EC2b --> Redis
EC2c --> Redis
ASG["ASG<br/>min:2 max:6"] -.->|"manage"| EC2a
ASG -.-> EC2b
ASG -.-> EC2c
style ALB fill:#e0e7ff,stroke:#4f46e5,color:#000
style ASG fill:#bfdbfe,stroke:#2563eb,color:#000High availability, horizontal scaling, multi-AZ. The most common pattern for web backends and traditional APIs.
EKS + EC2 Node Group — Full Power Kubernetes
graph TB
User["User"] --> ALB["ALB + Ingress Controller"]
ALB --> SvcA["Service A<br/>(3 replicas)"]
ALB --> SvcB["Service B<br/>(2 replicas)"]
ALB --> SvcC["Service C<br/>(4 replicas)"]
subgraph "EKS Cluster"
SvcA
SvcB
SvcC
end
SvcA --> RDS["Aurora"]
SvcB --> DDB["DynamoDB"]
SvcC --> SQS["SQS"] --> Worker["Worker Pods"]
style ALB fill:#e0e7ff,stroke:#4f46e5,color:#000Full control over nodes, efficient for stable and heavy workloads. Good for medium-to-large platforms with a team that understands Kubernetes.
EKS + Fargate — Serverless Kubernetes
graph TB
User["User"] --> ALB["ALB"]
subgraph "EKS + Fargate"
ALB --> PodA["Pod A<br/>(Fargate)"]
ALB --> PodB["Pod B<br/>(Fargate)"]
SQS["SQS"] --> PodC["Worker Pod<br/>(Fargate)"]
Cron["EventBridge<br/>Schedule"] --> PodD["Cron Pod<br/>(Fargate)"]
end
PodA --> RDS["Aurora Serverless"]
PodB --> RDS
style PodA fill:#bbf7d0,stroke:#16a34a,color:#000
style PodB fill:#bbf7d0,stroke:#16a34a,color:#000
style PodC fill:#bbf7d0,stroke:#16a34a,color:#000
style PodD fill:#bbf7d0,stroke:#16a34a,color:#000No worker nodes, minimal operations. Ideal for small teams or non-constant workloads.
Fargate + ECS — Event-Driven Worker
graph LR
S3["S3 Upload"] --> EB["EventBridge"]
SQS["SQS Queue"] --> ECS["ECS Service"]
Schedule["Cron Schedule"] --> EB
EB --> FG1["Fargate Task<br/>Image Processor"]
ECS --> FG2["Fargate Task<br/>Queue Worker"]
FG1 --> S3Out["S3 Output"]
FG2 --> DB["DynamoDB"]
style FG1 fill:#bbf7d0,stroke:#16a34a,color:#000
style FG2 fill:#bbf7d0,stroke:#16a34a,color:#000Event-driven, scale-to-zero, cost-efficient for sporadic workloads. No servers running when there’s no work.
The Most Common Architecture Mistakes
Based on experience across systems of various scales, here are the mistakes that repeatedly appear.
Using EKS for 2-3 services. The overhead of managing Kubernetes — RBAC, networking, monitoring, upgrades — isn’t worth the benefit for small systems. Use ECS + Fargate instead.
Using a single EC2 for production. One instance = one point of failure. At minimum use an ASG with min: 2 across two different AZs.
Running databases in Kubernetes. StatefulSets and PersistentVolumes do exist, but managing backups, failovers, and performance tuning for databases in K8s is far more complicated than using RDS or Aurora.
Using Fargate for all workloads without cost analysis. Workloads running 24/7 with high utilization are almost always cheaper on EC2 with Reserved Instances.
Choosing technology based on the resume, not the need. “We use EKS because it’s cool” isn’t an architecture reason. Every choice must be justifiable with concrete technical and business needs.
Summary
- EC2 — the classic virtual machine with full control. Good for legacy, stateful workloads, and experiments. Don’t use a single instance for production.
- ASG — not new compute, but a controller for EC2. Provides horizontal autoscaling and high availability. Applications must be stateless.
- EKS — managed Kubernetes for large-scale microservices. Powerful but complex. Only adopt when the team and scale are ready.
- Fargate — serverless containers without server management. Ideal for event-driven, background jobs, and small teams. Watch the cost for 24/7 workloads.
- The higher the abstraction, the smaller the operational burden — but the greater the per-unit cost and the more limited the control.
- There’s no “most correct” choice — only the most appropriate for your context. Use the decision tree to guide the decision.
- Hybrid is the realistic answer — many production systems combine EC2 (for steady-state heavy workloads) with Fargate (for event-driven and sporadic ones).
- Choose based on need, not hype — EKS for 2 services is over-engineering. A single EC2 for production is under-engineering. Find the sweet spot in between.