Serverless Without Bill Shock - Lambda Pricing and EBS Costs
“Serverless is cheap” - one of the most common oversimplifications in AWS cost conversations. Lambda is indeed cheap for low and medium traffic. The problem appears when traffic grows, functions get heavier, or someone enables Provisioned Concurrency “just in case”.
The second piece of this puzzle is EBS. It’s not serverless, but it accompanies almost every workload on AWS - from EC2 through ECS to EKS. Its pricing model is counter-intuitive: you pay for provisioned capacity (even if empty), for IOPS separately (with io2), and for snapshots - costs that grow without oversight.
How Lambda Pricing Works - The Billing Model
AWS Lambda charges across three dimensions:
1. Number of Requests
- $0.20 per 1 million requests (after free tier)
- Free tier: 1 million requests/month (perpetual, not just the first 12 months)
This is the simplest component and rarely causes problems. Even at 100 million invocations per month it’s “only” $20.
2. Execution Duration × Allocated Memory (GB-seconds)
This is the main Lambda cost driver:
- $0.0000166667 per GB-second (x86)
- $0.0000133334 per GB-second (ARM/Graviton2 - 20% cheaper)
- Free tier: 400,000 GB-seconds/month
How to calculate? A function with 512 MB memory executing for 200 ms:
0.5 GB × 0.2 s = 0.1 GB-s per invocation
Cost: 0.1 × $0.0000166667 = $0.00000167 per invocation
At 10 million invocations/month: $16.67/month - still cheap.
The problem appears when:
- Memory grows to 1-3 GB (image processing, ML inference, parsing large JSON files)
- Execution time grows to 5-30 s (external API calls, S3 operations, database connections)
- Both values increase simultaneously
| Scenario | Memory | Duration | Invocations/mo | Cost/mo |
|---|---|---|---|---|
| API gateway - lightweight endpoint | 128 MB | 50 ms | 10M | $1.04 |
| Webhook processing | 512 MB | 200 ms | 5M | $8.33 |
| Image resize | 1536 MB | 3 s | 500K | $37.50 |
| ETL / CSV parsing | 3008 MB | 15 s | 100K | $75.10 |
| ML inference (model in memory) | 10 GB | 10 s | 200K | $333.33 |
Takeaway: Lambda is cheap for short, lightweight functions. For heavy workloads (>1 GB memory, >5 s) Fargate or EC2 may be cheaper.
3. Provisioned Concurrency
Provisioned Concurrency eliminates cold starts but changes Lambda’s cost model from pay-per-use to a fixed cost:
- $0.0000041667 per GB per second of provisioning (charge for keeping “warm” instances)
- Plus standard execution charge (but lower: $0.0000097222 per GB-s)
Calculation: 10 instances × 1 GB × 24h × 30 days:
10 × 1 GB × 86,400 s × 30 = 25,920,000 GB-s provisioning
Cost: 25,920,000 × $0.0000041667 = $108/month
That’s $108/month regardless of whether those functions are invoked or not. Provisioned Concurrency is effectively a “hidden server” - you pay like EC2 but with a Lambda interface.
When Provisioned Concurrency makes sense:
- Functions on the critical path with <100 ms latency SLA
- Synchronous APIs where 1-5 s cold start is unacceptable
- Workloads with predictable, steady traffic
When it doesn’t make sense:
- Asynchronous processing (SQS → Lambda) - cold start doesn’t affect UX
- Low-traffic workloads (<100 invocations/h) - cold starts are rare
- Batch processing - a few extra seconds make no difference
Hidden Lambda Costs - What to Watch For
Data Transfer
Lambda in a VPC generates network traffic. When a function connects to RDS, S3 (via VPC endpoint), or an external API, transfer charges appear:
- Lambda → internet: $0.09/GB (eu-central-1)
- Lambda → another service in the same AZ: free
- Lambda → another service in a different AZ: $0.01/GB (both directions)
- Lambda → S3 (via gateway endpoint): free
- Lambda → S3 (via NAT Gateway): $0.045/GB + NAT charge
Most common mistake: Lambda in a VPC routing S3 traffic through a NAT Gateway instead of a VPC Endpoint (gateway). At 1 TB of transfer per month that’s $45 vs $0.
CloudWatch Logs
Every Lambda invocation logs to CloudWatch by default. Costs:
- $0.50 per GB ingested (log writes)
- $0.03 per GB stored (retention)
At 10M invocations with DEBUG-level logging, you easily generate 50-100 GB of logs monthly = $25-50 on ingestion alone.
Solution: set retention to 7-14 days for dev/test, 30 days for production. Archive to S3 (Glacier) if you need long-term storage.
Lambda@Edge and CloudFront Functions
If you use Lambda@Edge (e.g. for CDN response personalisation), pricing differs:
- $0.60 per 1M requests (3× more expensive than standard Lambda)
- $0.00000625125 per 128 MB-s (different rate)
- No free tier
CloudFront Functions (simpler, JavaScript only) are much cheaper: $0.10/1M requests.
Step Functions
If you orchestrate Lambdas via Step Functions:
- $25 per 1M state transitions (Standard Workflows)
- $0.025 per 1,000 requests (Express Workflows)
A complex workflow with 10 steps at 1M invocations/month: 10M transitions = $250/month. This can exceed the cost of the Lambdas themselves.
EBS Pricing - Why Disks Cost More Than You Think
EBS (Elastic Block Store) is block storage for EC2. It accompanies every workload that isn’t purely serverless - from standalone EC2 instances to EKS clusters with PersistentVolumes.
EBS Pricing Model
EBS charges across three dimensions:
| Dimension | gp3 | io2 | st1 (HDD) |
|---|---|---|---|
| Capacity ($/GB/mo) | $0.08 | $0.125 | $0.045 |
| IOPS ($/IOPS/mo) | 3,000 included, then $0.005 | $0.065 | n/a (throughput-based) |
| Throughput ($/MB/s/mo) | 125 MB/s included, then $0.04 | included with IOPS | $0.04 |
Key Principle: You Pay for Provisioned Capacity, Not Used Capacity
If you create a 500 GB gp3 volume but only write 50 GB of data - you pay for 500 GB:
500 GB × $0.08 = $40/month (regardless of utilisation)
This is the fundamental difference from S3, where you pay for data actually stored.
Common EBS Pitfalls
1. Over-provisioning Capacity
Developers create volumes “with headroom” - 500 GB per server that actually uses 80 GB. Across 20 servers:
- Provisioned: 20 × 500 GB = 10 TB → $800/mo
- Actually used: 20 × 80 GB = 1.6 TB → $128/mo
- Wasted: $672/mo (84% of storage budget)
Solution: Elastic Volumes - EBS allows increasing volume size without downtime. Start with what you need today and expand as required.
2. Snapshots Without Lifecycle Policy
EBS snapshots stored in S3 (managed by AWS) cost $0.05/GB/month. Seems modest, but:
- Daily snapshots of a 500 GB volume × 30 days = 15 TB (incremental, but growing)
- After a year without cleanup: easily 5-10 TB of snapshots = $250-500/month
Solution: AWS Backup with lifecycle rules - automatic deletion of snapshots older than 30/60/90 days. Or Data Lifecycle Manager (DLM).
3. io2 Where gp3 Would Suffice
io2 Block Express costs $0.065/IOPS/month. At 10,000 IOPS (typical for a database):
10,000 IOPS × $0.065 = $650/month (IOPS alone!)
+ capacity 200 GB × $0.125 = $25/month
= $675/month total
gp3 with 10,000 IOPS:
Baseline 3,000 IOPS included
Additional 7,000 × $0.005 = $35/month
+ capacity 200 GB × $0.08 = $16/month
= $51/month total
Difference: $624/month on a single volume. io2 only makes sense when you need >16,000 IOPS or guaranteed 99.999% durability.
4. Orphaned (Unattached) Volumes
Detached EBS volumes still incur charges. After EC2 termination, volumes with DeleteOnTermination: false remain. In Cost Explorer look for: UsageType: EBS:VolumeUsage with a LinkedAccount but no associated instance.
Solution: AWS Trusted Advisor or aws ec2 describe-volumes --filters Name=status,Values=available - a list of volumes to delete.
Lambda vs Fargate vs EC2 - When Does Each Make Sense
Not every workload should be serverless. Here’s a cost comparison for the same workload:
Scenario: API Handling 1,000 req/s, 100 ms Latency, 512 MB RAM
| Solution | Configuration | Cost/month | Notes |
|---|---|---|---|
| Lambda | 512 MB, 100 ms, 2.6B req/mo | ~$2,200 | Scales perfectly, zero management |
| Fargate | 4 tasks × 1 vCPU + 2 GB | ~$580 | Fixed cost, requires scaling management |
| EC2 (on-demand) | 2× m6i.large | ~$280 | Cheapest, but OS management and patching |
| EC2 (Savings Plan) | 2× m6i.large, 1Y SP | ~$185 | Commitment required, but lowest cost |
Break-even point: Lambda is cheaper than Fargate up to ~200 req/s sustained traffic (at 100 ms execution). Above that threshold - Fargate or EC2.
When Lambda Is Cost-Optimal
- Sporadic or spiky traffic - an API with 50 req/s at peak but 0.1 req/s at night. Lambda scales to zero.
- Event-driven processing - SQS/SNS → Lambda. You pay only for actual processing.
- Cron jobs - instead of a 24/7 EC2 for a single cron at 3 AM.
- Prototyping - MVP without infrastructure commitments.
When Lambda Gets Expensive
- Constant high traffic - >500 req/s sustained → EC2 or Fargate are cheaper.
- Long execution - functions >30 s → Fargate (max 15 min) or ECS.
- High memory usage - >3 GB → EC2 with proper right-sizing.
- GPU workloads - Lambda doesn’t support GPUs.
Lambda Cost Optimisation - Practical Techniques
1. Memory Right-Sizing
Lambda automatically scales CPU proportionally to memory. More memory = more CPU = faster execution. Paradoxically, increasing memory can lower cost:
Function with 128 MB, execution 3,000 ms:
0.128 GB × 3 s = 0.384 GB-s = $0.0000064 per call
Function with 512 MB, execution 800 ms (more CPU):
0.512 GB × 0.8 s = 0.4096 GB-s = $0.0000068 per call
Marginally more expensive per call, but 3× faster. For the user - better UX.
Tool: AWS Lambda Power Tuning (open-source, SAR) - tests your function with different memory configurations and shows the optimal price/performance point.
2. ARM (Graviton2) Instead of x86
Lambda on ARM is 20% cheaper per GB-s and typically comparably fast. Migration is seamless for:
- Python, Node.js, Java, .NET 6+ - no code changes required
- Native binaries - require recompilation
The change is one line in configuration: Architectures: [arm64]
3. Avoiding Cold Starts Without Provisioned Concurrency
- Keep deployment packages small - less code = faster cold start
- Lazy initialisation - don’t load everything at startup
- SnapStart (Java) - eliminates cold start for Java at no additional cost
- Ping/warmup - EventBridge invokes the function every 5 min (cost: practically zero)
4. Batching Instead of Individual Invocations
Instead of 1,000 separate Lambda invocations processing one record each from SQS, set BatchSize: 100 and process 10 invocations of 100 records each. Savings on request fees plus less cold start overhead.
EBS Cost Optimisation - Practical Techniques
1. Migrate from gp2 to gp3
gp3 is 20% cheaper than gp2 by default and provides better baseline performance (3,000 IOPS + 125 MB/s vs size-dependent scaling in gp2).
Migration requires no downtime - volume type change happens live:
aws ec2 modify-volume --volume-id vol-xxx --volume-type gp3
Savings across 100 volumes × 200 GB:
- gp2: 100 × 200 × $0.10 = $2,000/mo
- gp3: 100 × 200 × $0.08 = $1,600/mo
- Savings: $400/mo with zero performance impact
2. Automated Snapshot Cleanup
# Find snapshots older than 90 days
aws ec2 describe-snapshots --owner-ids self \
--query "Snapshots[?StartTime<='2026-05-04'].SnapshotId" \
--output text
Better: configure AWS Backup with a retention policy or Data Lifecycle Manager.
3. Identify Orphaned Volumes
# Volumes in "available" state (detached)
aws ec2 describe-volumes \
--filters Name=status,Values=available \
--query "Volumes[].{ID:VolumeId,Size:Size,Created:CreateTime}" \
--output table
Every volume on this list is money wasted - not attached to any instance, yet AWS keeps billing.
4. Right-Size IOPS
Check actual IOPS utilisation in CloudWatch (VolumeReadOps + VolumeWriteOps). If an io2 volume with 10,000 IOPS actually uses max 4,000 - switch to gp3 with 4,000 IOPS and save $600+/month.
Cost Monitoring - What to Track
For Lambda
In CloudWatch Metrics monitor:
- Duration (p99) - if it grows, cost grows proportionally
- ConcurrentExecutions - if approaching the limit, rethink architecture
- Throttles - mean lost invocations and potentially higher costs (retries)
- IteratorAge (for stream triggers) - processing delay
In Cost Explorer:
- Filter: Service = Lambda, Group by: Function Name
- Identify the top 5 most expensive functions and optimise them
For EBS
In Cost Explorer:
- Filter: Usage Type = EBS:VolumeUsage.gp3 (or io2, gp2)
- Group by: Availability Zone - check for balanced distribution
In CloudWatch:
- VolumeReadOps / VolumeWriteOps - are provisioned IOPS being utilised?
- BurstBalance (gp2) - if it drops to 0, you need gp3 or io2
Real-World Scenario: SaaS Company - Lambda + EBS Optimisation
Starting Position
A SaaS company with an API on Lambda (20 functions, ~5M invocations/day) + backend on ECS with EBS volumes. Monthly bill: $4,800.
Cost breakdown:
| Service | Cost/mo | Share |
|---|---|---|
| Lambda | $1,200 | 25% |
| EBS | $1,400 | 29% |
| ECS Fargate | $1,500 | 31% |
| Other (NAT, CloudWatch, S3) | $700 | 15% |
Issues Identified
- Lambda - 3 functions accounted for 70% of Lambda costs. One of them (image processing) had 3 GB memory and 8 s execution.
- EBS - 12 gp2 volumes at 500 GB each, actual usage: 60-120 GB each. Plus 4 TB of stale snapshots.
- NAT Gateway - Lambda in a VPC routing S3 traffic through NAT instead of a VPC Endpoint.
Optimisation Results
| Change | Monthly Savings |
|---|---|
| Image processing Lambda → Fargate task | -$380 |
| Migrate 12× EBS from gp2 to gp3 | -$120 |
| Resize EBS from 500 GB to 150 GB (Elastic Volumes) | -$336 |
| Remove stale snapshots + DLM (30-day retention) | -$180 |
| S3 VPC Endpoint (eliminate NAT) | -$95 |
| Lambda ARM migration (15 functions) | -$180 |
| Reduce CloudWatch log retention (30 days) | -$65 |
Total savings: $1,356/month (28% of the bill)
Implementation time: 2 days of DevOps engineer work.
Savings Plans and Lambda
Compute Savings Plans cover Lambda. If you have stable Lambda usage ($500+/month), a Savings Plan gives an additional 17% discount on duration charges.
Calculation:
- Stable Lambda usage: $800/month
- Compute SP commitment: $0.37/h (≈ $270/month commitment towards Lambda)
- Discount: 17% on the covered portion
- Savings: ~$46/month
At low Lambda spend (<$200/mo), Savings Plans don’t make sense solely for Lambda. But if you run a mix of Lambda + EC2 + Fargate - a Compute SP covers everything with a single commitment.
Summary - Optimisation Checklist
Lambda
- Run Lambda Power Tuning on the top 5 most expensive functions
- Migrate to ARM (Graviton2) where possible
- Verify Provisioned Concurrency is actually needed
- Set up an S3 VPC Endpoint (gateway) - eliminates NAT costs
- Reduce CloudWatch log retention (7-30 days)
- Consider Fargate/ECS for functions >3 GB memory or >30 s execution
- Use batching (SQS BatchSize) instead of many individual invocations
EBS
- Migrate all gp2 volumes to gp3 (zero downtime, 20% cheaper)
- Right-size volumes - check actual usage vs provisioned
- Delete unattached volumes (
status=available) - Configure DLM or AWS Backup with snapshot retention policy
- Check if io2 is needed - gp3 with 16K IOPS covers most use cases
- Tag volumes (Owner, Environment) - without tags you can’t identify the culprit in Cost Explorer
How We Can Help
At Devopsity we run AWS cost audits with a particular focus on serverless and storage - the two areas that most often grow unchecked.
- Lambda audit (1 day) - identify most expensive functions, right-sizing and architecture recommendations
- EBS/storage audit (1 day) - map over-provisioned volumes, orphaned snapshots, wrong volume types
- Optimisation implementation (1-3 days) - gp2→gp3 migration, Elastic Volumes, DLM, VPC Endpoints, ARM migration
- FinOps dashboard (1 day) - automated cost anomaly alerts, per-team reports
AWS bill growing?
Book a free 30-minute consultation. We'll show you how much you can save on Lambda and storage without changing your architecture.
Frequently Asked Questions
Does Lambda have a free tier?
Yes, and it’s perpetual (not just the first 12 months): 1 million requests + 400,000 GB-seconds per month. For a typical side-project or internal tool - that’s enough for zero cost.
What is Lambda’s maximum execution time?
15 minutes (900 seconds). If your function regularly approaches this limit, consider Fargate (no time limit) or Step Functions (orchestrating shorter steps).
Is gp3 always better than gp2?
Almost always. gp3 is cheaper ($0.08 vs $0.10/GB), provides higher baseline IOPS (3,000 vs size-dependent), and has no burst balance mechanism. The only edge case: volumes <170 GB on gp2 may theoretically have higher peak burst, but in practice gp3 is better.
How do I check what a specific Lambda function costs?
In Cost Explorer: filter Service = Lambda, Group by = Resource (requires tagging). Or use aws ce get-cost-and-usage with a filter on the aws:lambda:function:name tag.
Are EBS snapshots incremental?
Yes. The first snapshot copies the entire volume; subsequent ones only copy blocks changed since the last snapshot. But storage charges are for the sum of all unique blocks across all snapshots - if you don’t delete old ones, costs grow.
Can I shrink an EBS volume?
Not directly. AWS only allows increasing volumes (Elastic Volumes). To shrink, you must: create a new smaller volume → copy data → attach the new one → delete the old one. That’s why starting with a smaller size is important.