Serverless is a wonderful default for new workloads. It is also, regularly, the wrong tool for the job. Here is how we reason about the choice.
The three axes that matter
Ignore ideology. Decide on workload shape, cost curve, and team capability.
Axis 1: Workload shape
- Bursty / unpredictable / low-average traffic: Lambda wins. You pay nothing when idle and the scale-out is free.
- Steady, predictable, sustained traffic: ECS Fargate or EC2 is usually 30–60% cheaper per request once you're past ~5M invocations/day at meaningful execution durations and memory allocations.
- Long-running jobs (>15 min): Lambda is out. Use ECS or Step Functions driving Fargate tasks or chunked Lambda steps.
- Memory-heavy workloads (>10 GB): Lambda caps at 10,240 MB. ECS or EC2.
- Stateful / sticky sessions: servers, or re-architect to be stateless and reach for Lambda.
Axis 2: The cost curve
Lambda pricing is linear in invocations and execution time. Server pricing is step-function: a single small Graviton instance (t4g or m7g) handles thousands of requests per second for a fixed monthly cost. Plot both curves and find the crossover.
Rough rule of thumb: if a function runs less than ~30% of wall-clock time, Lambda is cheaper. If it's busy most of the time, servers win.
A workload that is busy most of the time can cost several times more on Lambda than on a pair of small always-on instances. Plot the two curves before committing to either.
Axis 3: Team capability
Serverless is a different operating model. Cold starts, IAM-per-function, CloudWatch logs structure, distributed tracing: these are all second nature to some teams and completely foreign to others. If your team is most productive in a traditional Node/Python/Go service on a Linux box, don't force Lambda on them for the sake of it.
Conversely, if your team has embraced infrastructure-as-code, prefers small single-purpose functions, and already lives in AWS, Lambda's operational simplicity is a gift.
Common wins for Lambda
- Event-driven glue (S3 trigger → transform → DynamoDB).
- Cron-style batch jobs.
- Internal tools and admin APIs with low traffic.
- Webhooks with bursty traffic (Stripe, GitHub, Twilio).
- Image and document processing pipelines.
Common wins for traditional servers
- High-throughput APIs with steady traffic (>100 req/s sustained).
- WebSocket servers and real-time connections.
- Workloads needing custom kernel tuning or GPUs.
- Background workers with very long-running jobs.
- Legacy applications not designed for stateless execution.
The middle path: Fargate
People forget this option. ECS Fargate gives you containers without EC2 management: serverless-adjacent operations, but with the economics of traditional servers for steady workloads. For many production APIs, Fargate is the right answer.
A pragmatic rubric
| If your workload is… | Start with |
|---|---|
| Event-driven glue, under 15 min | Lambda |
| Steady API, >100 req/s | ECS Fargate |
| Real-time / WebSocket | ECS or EC2 |
| Bursty webhooks | Lambda |
| GPU or specialty hardware | EC2 |
Torn between Lambda, Fargate and EC2 for your next workload? Describe it: a one-page diagnosis within 48 hours.
Get my diagnosis →The short list
- Ignore ideology. Decide on shape, cost, and team.
- Lambda wins on bursty, short, low-duty-cycle work.
- Servers win on steady, long, or stateful work.
- Fargate is often the right middle path.
- Run the cost curve; don't guess.