Overview
AWS compute spans the same control-versus-convenience spectrum every cloud provider offers: EC2 gives full OS-level control as plain virtual machines, Lambda gives fully serverless, event-driven functions with no server to manage at all, and ECS/EKS give managed container orchestration, AWS's own native model or a real managed Kubernetes control plane, respectively. Picking between them is a workload-fit decision: a short-lived, event-driven task is usually a worse fit for a hand-managed EC2 fleet than for Lambda, while a workload needing custom OS-level tuning, unsupported dependencies, or execution beyond Lambda's 15-minute ceiling genuinely needs EC2 regardless of how "modern" the alternatives look.
Quick Reference
| Service | Model | You manage | Best fit |
|---|---|---|---|
| EC2 | IaaS | OS, runtime, scaling, patching | Custom OS/runtime needs, full control, long-running processes |
| Lambda | Serverless / FaaS | Function code only | Event-driven, short-lived work (≤15 min per invocation) |
| ECS | Managed orchestration (AWS-native) | Task definitions, container images | Containerized workloads without adopting Kubernetes |
| EKS | Managed Kubernetes | Nodes/workload compute unless using Fargate or EKS Auto Mode (control plane billed separately) | Kubernetes-based orchestration, portability, ecosystem |
Syntax
# Lambda - deploy a function without managing any server
aws lambda create-function \
--function-name process-orders \
--runtime nodejs20.x --handler index.handler \
--role arn:aws:iam::111122223333:role/lambda-orders-exec \
--zip-file fileb://function.zip# ECS - run a task definition on serverless Fargate capacity,
# no EC2 instances to patch or scale for this workload.
aws ecs run-task \
--cluster app-cluster --launch-type FARGATE \
--task-definition orders-api:3Examples
# EC2 Auto Scaling Group - scale on a custom CloudWatch metric
# (queue depth), not just CPU, when that's the real bottleneck.
aws autoscaling put-scaling-policy \
--auto-scaling-group-name orders-worker-asg \
--policy-type TargetTrackingScaling \
--target-tracking-configuration file://queue-depth-target.jsonStart with the most managed option that fits
Default to Lambda or ECS/EKS for new workloads, and drop to plain EC2 only when you hit a specific, genuine requirement it can't satisfy, not as a default starting point.
Visual Diagram
Common Mistakes
- Defaulting to EC2 out of familiarity for an event-driven, short-lived workload that Lambda would handle with far less operational burden.
- Choosing EKS for a simple containerized workload without a real need for Kubernetes portability, then paying its per-cluster control-plane fee on top of the added operational surface.
- Not accounting for Lambda's 15-minute execution ceiling and cold-start latency when picking it for a long-running or strictly latency-sensitive workload.
- Scaling an EC2 Auto Scaling Group on CPU alone when the actual bottleneck is queue depth, memory, or a downstream dependency, leaving the workload effectively under-provisioned despite autoscaling being "on."
Performance
- Lambda cold starts add real latency on infrequently-invoked functions; provisioned concurrency removes this at an added, standing cost.
- ECS and EKS worker-node performance is standard EC2 instance performance unless the workload runs on Fargate or, for EKS, EKS Auto Mode, in which case AWS manages that compute; instance-type and sizing choices matter exactly as much as they would running EC2 directly.
- Auto Scaling Group scale-out has real instance warm-up time; very spiky traffic may need a lower scale-out threshold or pre-warmed capacity to stay ahead of demand.
Best Practices
- Choose compute by execution model first, event-driven and short-lived → Lambda, containerized → ECS/EKS, needs OS-level control or long-running state → EC2, rather than by habit.
- Use EKS specifically when Kubernetes's API and ecosystem portability matter; use ECS when a simpler, AWS-native model is enough.
- Scale on the metric (including custom CloudWatch metrics) that actually reflects the workload's bottleneck, not CPU by default.
- Revisit the compute choice as a workload matures; constraints that justified EC2 early on may no longer apply once the workload's shape is well understood.