AWS gives you three layers of compute, each with a different cost model and ops profile. Picking the wrong layer for the workload is the single most common AWS cost-explosion we’re asked to fix. Get this decision right and your AWS bill stays predictable; get it wrong and a Lambda function ends up costing 10x what an EC2 instance would.

Lambda: serverless, per-invocation pricing
Lambda is perfect for bursty, event-driven workloads where utilization is low and predictability is high. Cron-triggered ETL. Webhook receivers. Image-resize on upload. S3 event processors.
Where Lambda goes wrong: if you’re invoking it constantly (steady-state traffic), the per-invocation pricing crosses over to expensive vs an always-on container or VM by about 10-15% utilization. Many teams discover this when their Lambda bill jumps after a product launch and they have to rearchitect to Fargate.
Use Lambda when
- Workload is event-triggered, not request-triggered
- Invocation rate is <5 per second on average
- Cold-start latency (100-500ms) is acceptable for your use case
- The work completes in <15 minutes per invocation
- Ops burden has to be near-zero (no instances to manage)
Fargate: containers, no instances
Fargate is the sweet spot for steady-state services. You bring a container image, AWS runs it on hidden infrastructure, you pay per vCPU-second and per GB-second. No instance management, no patching, no capacity planning, but also no cold start penalty after the first task starts.
For most SaaS workloads, Fargate is the right answer. Steady traffic, predictable scaling, no DevOps team needed. The unit cost is higher than EC2 with reserved instances, but the saved engineering hours usually more than compensate.
Use Fargate when
- You have steady or predictably-scaling traffic
- You don’t want to manage EC2 instances
- Your team doesn’t have a dedicated platform engineer
- You’re running containers (you should be)
EC2: the raw VM
EC2 makes sense when you have high steady-state utilization (where reserved instances or savings plans pay off hard), specialized hardware needs (GPUs, large memory, NVMe), or stateful workloads that don’t fit cleanly into containers (think: classic database servers, lift-and-shift legacy apps).
The ops burden is real: you patch the OS, you handle instance failures, you size for peak. But the unit cost at 70%+ utilization beats Fargate by a wide margin, and beats Lambda by an order of magnitude.
Use EC2 when
- Steady-state utilization is >60% and you can reserve capacity
- You need GPUs, large memory (768GB+), or local NVMe
- You have a platform team that can operate it
- You’re running stateful workloads (DBs, caches with persistence)
The crossover math
Quick mental model:
- Lambda is cheapest below ~10% utilization (i.e. invocation rate equivalent to running a small container 10% of the time)
- Fargate wins from ~10% to ~60% utilization
- EC2 with reserved/savings wins above ~60% utilization
These numbers shift with workload shape, but the order is right. Pick the layer that matches where your utilization will land in 12 months, not where it is on day one.
Mixing layers is normal
Most production AWS deployments we run mix all three. Web tier on Fargate. Event-driven jobs on Lambda. Database on EC2 (or RDS, which is EC2 under the hood). Each piece picks the layer that matches its profile. Don’t feel obliged to consolidate.
How we approach this
For the cloud architectures we ship via Cloud & DevOps engagements, we default to Fargate for app tiers and Lambda for event handlers. We introduce EC2 only when there’s a documented need (utilization >60%, specialized hardware, stateful workloads). This combination gives our clients the most operational simplicity per dollar spent.
Takeaways
- Lambda for event-driven, low-utilization workloads.
- Fargate for steady-state services without an ops team.
- EC2 for high utilization, specialized hardware, or stateful workloads.
- Crossover: Lambda < 10% < Fargate < 60% < EC2.
- Mix all three in one deployment. That’s normal.







