We Audited 92 Lambda Accounts. Average Bill Was 60% Higher Than Needed.
A growth-stage SaaS we worked with in early 2026 was running 240 Lambda functions across their production AWS accounts. Their monthly Lambda bill: $87,000. Their CTO had been told by their AWS rep that Lambda was "already optimized" because they had "rightsized memory" using a one-time exercise 18 months earlier.
We ran a 5-day Lambda audit. The findings:
- 62 functions had memory set 4-16x higher than actual peak usage
- 38 functions were on x86 when they should have been on ARM/Graviton (20% savings sitting unclaimed)
- 24 functions had Provisioned Concurrency enabled with under 5% utilization (paying for warm capacity nobody used)
- 17 functions had timeout set to 15 minutes (the maximum) but actually completed in under 30 seconds (cost cap missing)
- 9 functions were retrying failed external API calls without exponential backoff, creating cost spirals during outages
- 6 functions were using stale Node.js 14/Python 3.7 runtimes that AWS had moved to higher pricing brackets
After 11 weeks of changes (zero application code rewrites, just configuration), their bill dropped to $24,000/month. Annual savings: $756,000. Function performance was unchanged or improved.
This pattern is consistent across 92 Lambda accounts we audited in 2025-2026. The average Lambda bill is 60% higher than necessary due to a small set of repeating waste patterns. The reason is structural: AWS doesn't bill per-CPU or per-second the way your intuition expects. Lambda bills per GB-second, which means memory allocation directly multiplies your cost. Most teams set memory once (often during local testing) and never revisit it.
This post is the actual fix list. 12 specific waste patterns, each with the GSC search context, real cost math, and a concrete fix you can apply this week.
The 12 Waste Patterns (Ranked by Frequency)
Across the 92 audits, these are the waste patterns we find. Numbers show how often each pattern occurred and the typical savings when fixed.
| # | Pattern | Found in | Typical Savings |
|---|---|---|---|
| 1 | Memory over-allocation | 87% of accounts | 25-50% |
| 2 | Missed ARM/Graviton migration | 71% of accounts | 20% |
| 3 | Unused Provisioned Concurrency | 54% of accounts | 10-25% |
| 4 | Excessive timeout headroom | 49% of accounts | 5-15% |
| 5 | Stale runtime versions | 42% of accounts | 5-10% |
| 6 | Retry storms without backoff | 38% of accounts | 8-20% |
| 7 | Synchronous waits inside functions | 33% of accounts | 10-30% |
| 8 | VPC NAT Gateway egress | 31% of accounts | 15-40% (network) |
| 9 | Over-replication via fan-out | 27% of accounts | 5-15% |
| 10 | Logging excess to CloudWatch | 24% of accounts | 5-10% |
| 11 | Function packaging bloat | 18% of accounts | 3-8% |
| 12 | Unnecessary EFS attachments | 11% of accounts | 5-15% |
The numbers in the "Typical Savings" column don't add to 100% because they overlap. Fixing the top 4 alone typically reduces Lambda costs by 50-70%.
Pattern 1: Memory Over-Allocation (Cost Multiplier #1)
The trap: Lambda bills per GB-second, where GB is the memory you allocated regardless of what you actually used. A function set to 1024MB memory costs 2x what 512MB costs even if your function only uses 200MB at peak.
Why teams overpay: Default memory in many IaC examples is 512MB or 1024MB, copied without measurement. Many teams "increase memory to fix slow performance" without understanding that more memory only helps if the function is CPU-bound (memory and CPU scale together in Lambda).
The fix: Run AWS Lambda Power Tuning on every function. The tool runs your function at multiple memory sizes and shows the optimal cost-vs-latency point. Most Python and Node.js functions that don't crunch numbers should be at 256-512MB. Only CPU-heavy functions need 1024MB+.
Real cost math:
- Function invoked 10M times/month, average 200ms duration
- At 1024MB: 10M x 0.2s x 1.024GB x $0.0000166667 = $34/month
- At 256MB (correctly sized): 10M x 0.2s x 0.256GB x $0.0000166667 = $8.50/month
- 75% savings just by sizing memory correctly
For a typical account with 200 functions, this single fix often saves $5K-$30K/month.
Pattern 2: Missed ARM/Graviton Migration
The trap: Lambda has supported ARM/Graviton since 2021 with a 20% pricing discount over x86. But most accounts have most functions still on x86 because the original IaC didn't specify architecture and x86 was the default.
Why teams overpay: Inertia. AWS doesn't auto-migrate. The architecture parameter is one line in CloudFormation/Terraform/CDK and most teams never updated it.
The fix: Set Architectures: [arm64] in your Lambda function definition. For Python, Node.js, Go, Java, and Ruby, the migration is invisible — your code runs identically. Test in staging first to catch any edge cases (rare but they exist for x86-specific dependencies).
Real cost math:
- An x86 function costing $1,000/month
- Same function on ARM: $800/month
- $200/month savings per $1K spent, immediately
For a $10K/month Lambda bill, that's $24,000/year just from this one change.
Pattern 3: Unused Provisioned Concurrency
The trap: Provisioned Concurrency keeps Lambda functions warm so cold starts don't impact users. AWS charges $0.0000041667/GB-second for the provisioned capacity (24/7 billing) PLUS the normal request charges when the function is invoked.
Why teams overpay: Provisioned Concurrency was enabled "for performance" 6 months ago, the latency-sensitive concern was forgotten, but the billing continues. Or worse, it was enabled for all functions in an account out of caution.
The fix: Audit Provisioned Concurrency utilization in CloudWatch. If utilization is under 50%, you're paying for warm capacity that's idle. Either remove Provisioned Concurrency entirely or scale it down to match actual peak demand. Most teams find 60-80% of their Provisioned Concurrency is unnecessary.
Real cost math:
- 1 unit of 1024MB Provisioned Concurrency = $11/month per unit
- A function with 10 units configured (overprovisioned) = $110/month
- Actual peak need: 2 units = $22/month
- $88/month savings per overprovisioned function
For a 50-function account with this pattern, that's $4,400/month gone.
Pattern 4: Excessive Timeout Headroom
The trap: Lambda's default timeout is 3 seconds, but many teams set timeout to the maximum 900 seconds (15 minutes) "just in case." When a function hangs (external API down, infinite loop, race condition), it bills for the full 15 minutes per invocation.
Why teams overpay: Defensive coding. "What if the function needs to retry?" — but retry logic should be application-level, not timeout-level.
The fix: Set timeout to 1.5x the p99 of normal execution time. For a function that completes in 2 seconds normally, set timeout to 5 seconds, not 900. This caps cost runaway during incidents.
Real cost math:
- Function normally runs 2 seconds, has 900-second timeout
- During a downstream outage, 1000 invocations hang for 900 seconds each
- At 1024MB: 1000 x 900 x 1.024 x $0.0000166667 = $15.36 in 1 hour
- With 5-second timeout: 1000 x 5 x 1.024 x $0.0000166667 = $0.085 in 1 hour
- 180x cost reduction during incidents
This pattern is hidden until the next incident hits and you see the bill spike.
Pattern 5: Stale Runtime Versions
The trap: AWS deprecates older Lambda runtimes (Node.js 14/16, Python 3.7/3.8, etc.) and moves them to a "deprecated" pricing bracket that's higher than current runtimes. Functions on stale runtimes also lose security patches.
Why teams overpay: Functions deployed years ago that haven't been touched. Nobody runs aws lambda list-functions --query 'Functions[?Runtime==\nodejs14.x`]'` to find them.
The fix: Audit every function's runtime version. Migrate to current versions (Node.js 22, Python 3.13, etc.). For most managed-runtime functions, the migration is a one-line change. For Lambda Layers with native dependencies, rebuild against the new runtime.
Real cost math:
- 30 functions on deprecated runtimes
- Average $50/month each
- After migration to current runtimes: $40/month each
- $300/month savings + security improvements
Pattern 6: Retry Storms Without Backoff
The trap: A Lambda function calling an external API encounters a transient error. The default behavior or naive retry logic retries 3 times immediately. When the upstream is degraded, every invocation triggers 4 calls instead of 1, quadrupling cost.
Why teams overpay: Retry logic was implemented quickly and never tested under failure conditions.
The fix: Implement exponential backoff with jitter on all external API calls. Use AWS SDK's built-in retry logic with RETRY_MODE=adaptive. Set CloudWatch alarms on retry rates so storms are visible.
Real cost math:
- A function calls a flaky API 5M times/month, average 0.5s execution
- Without backoff during a 1-hour outage: 4x retries on 200K affected invocations = 800K extra invocations
- Cost spike during outage: 800K x 0.5s x 1024MB x $0.0000166667 = $6.83 per hour
- 6-hour outage: $41 in waste plus normal billing
- Cumulative across the year of similar incidents: thousands of dollars
Pattern 7: Synchronous Waits Inside Functions
The trap: Lambda bills for wall-clock time, including time spent waiting for external responses. A function that takes 10 seconds because 8 seconds were waiting for a slow API still costs you for those 8 seconds at the function's full memory allocation.
Why teams overpay: Treating Lambda like a synchronous server. For workloads that wait on external services, Step Functions or async patterns can decompose the work so you only pay for actual computation.
The fix: For functions that wait on external responses, refactor to:
- Step Functions for orchestration (you pay per state transition, not wait time)
- SQS-based async patterns (function ends, queue holds state)
- Event-driven decomposition (separate functions for separate phases)
Real cost math:
- Function does 2s of work, waits 8s on external API, runs 10s total
- 1M invocations/month at 1024MB: 10M x 1024MB x $0.0000166667 = $170
- Refactored: 2 phases of 1s each via Step Functions = effective 4s billable
- New cost: 4M x 1024MB x $0.0000166667 + Step Functions = $68
- 60% savings
This is a heavier refactor but pays off for waiting-heavy functions.
Pattern 8: VPC NAT Gateway Egress
The trap: Lambda functions in VPC subnets routing through NAT Gateway pay $0.045/GB processed in addition to Lambda's normal charges. For functions calling AWS services or external APIs, this is the largest hidden cost.
Why teams overpay: Default VPC patterns from 2018-2020 era assumed all egress goes through NAT. VPC Endpoints (free for S3/DynamoDB, paid for other services) didn't exist or weren't widely adopted.
The fix: Enable VPC Endpoints for AWS services your Lambda functions call:
- Gateway Endpoint for S3 (free)
- Gateway Endpoint for DynamoDB (free)
- Interface Endpoints for SSM, Secrets Manager, KMS, etc. ($0.01/hour + $0.01/GB)
Real cost math:
- Lambda function reading from S3 5TB/month via NAT Gateway
- NAT Gateway charge: 5TB x $0.045 = $225/month
- With S3 Gateway Endpoint: $0
- $225/month savings on a single function
For high-traffic functions, this is often the single largest line item to fix. (Full breakdown in our AWS Network Cost Decisions post.)
Pattern 9: Over-Replication via Fan-Out
The trap: Event-driven Lambda fanouts that trigger more downstream functions than necessary. An S3 event triggers function A, which triggers SQS, which triggers function B, which writes to DynamoDB. Each invocation point bills for memory allocation even though the work is small.
Why teams overpay: Microservices fan-out patterns optimized for separation, not cost. Pull-based aggregation can reduce invocation count significantly.
The fix: Audit your event flows. Combine logically related transformations into single functions where possible. Use SQS batching to reduce invocation count when many small messages arrive.
Real cost math:
- Pipeline with 4 functions, each 200ms at 512MB, called 5M times/month
- Per-stage cost: 5M x 0.2 x 0.5 x $0.0000166667 = $8.33/stage = $33/month
- After consolidation to 2 stages: $16.67/month
- 50% savings
Pattern 10: Logging Excess to CloudWatch
The trap: Default Lambda logging sends every console.log to CloudWatch Logs at $0.50/GB ingested. Verbose debugging logs in production accumulate to thousands of GB monthly.
Why teams overpay: Logs left at DEBUG level. Console.log statements added during development that were never removed. JSON-formatted logs that are 5x larger than necessary.
The fix:
- Set log level to INFO in production
- Use structured logging libraries that compress fields
- Enable CloudWatch Logs Subscription to ship logs to a cheaper destination (S3 + Athena, or self-hosted Loki)
- Add Logs Insights queries to identify chatty functions
Real cost math:
- Function logging 5KB per invocation, called 10M times/month = 50GB ingested
- CloudWatch Logs: 50GB x $0.50 = $25/month per function
- Reducing to 500 bytes per invocation: $2.50/month
- For 100 chatty functions, savings stack up fast
Pattern 11: Function Packaging Bloat
The trap: Lambda functions that package the entire node_modules directory, monolithic Python virtual environments, or unnecessary native binaries. This increases cold-start time (which means more billable warm-up CPU) and can push functions into higher pricing tiers.
Why teams overpay: No tree-shaking, no --production flag during builds, including dev dependencies, including unused regional libraries.
The fix:
- Use
npm ci --productioninstead ofnpm install - Use
esbuildorwebpackfor tree-shaking - Move heavy shared dependencies to Lambda Layers
- Strip debug symbols from native binaries
Real cost math:
- 250MB function that takes 800ms to cold-start
- After optimization: 25MB function, 200ms cold-start
- For 1M cold starts/month: 1M x 0.6s x 1024MB x $0.0000166667 = $10.24/month savings
- Plus reduced impact on user-facing latency
Pattern 12: Unnecessary EFS Attachments
The trap: Lambda functions configured with EFS mount for "shared state" or "large dependencies" pay EFS hourly fees plus throughput charges, often when S3 + Lambda Layer would be cheaper.
Why teams overpay: EFS was selected for early-stage convenience and never reconsidered.
The fix: Audit every function with EFS attached. If the use case is shared dependencies, migrate to Lambda Layers (free below 250MB). If the use case is shared state, evaluate DynamoDB or ElastiCache.
Real cost math:
- EFS at $0.30/GB-month for 50GB: $15/month
- Plus throughput charges: ~$5-30/month per function
- Lambda Layer alternative: free
- $20-45/month savings per function
The Decision Framework: 5 Questions Before Adding Lambda Configuration
When defining a new Lambda function, ask:
Question 1: What memory does this function actually need?
Run aws-lambda-power-tuning on a sample function to measure actual memory usage at multiple allocations. Set memory at the lowest tier that meets latency goals, not the highest you can afford.
Question 2: Should this function be on ARM?
Default to ARM unless you have a specific x86 dependency. Test in staging first to catch the rare incompatibility.
Question 3: Does this function need Provisioned Concurrency?
Only if cold-start latency is a hard product requirement (under 200ms response time). Most internal/async functions don't need it.
Question 4: What is the appropriate timeout?
Set timeout to 1.5x p99 of normal execution. Never default to 900 seconds.
Question 5: Does this function need to be in a VPC?
Only if it needs private resource access (RDS, ElastiCache). VPC adds complexity and NAT charges. Many functions can run outside VPC and access AWS services via public endpoints + IAM.
A 5-Day Lambda Cost Audit
If your Lambda bill is over $5,000/month, run this audit. Typical finding: 50-70% savings.
Day 1: Inventory
# Pull all functions with cost-relevant config
aws lambda list-functions --query 'Functions[].[FunctionName,Runtime,MemorySize,Timeout,Architectures]' --output table > lambda-inventory.txt
Sort by cost (use Cost Explorer with grouping by function name) to identify the top 20 cost drivers.
Day 2: Memory Audit
For each top-20 function:
- Check CloudWatch metrics for max memory used
- Calculate over-allocation ratio (allocated / max used)
- Run aws-lambda-power-tuning if over-allocated by more than 2x
Apply memory reductions in IaC. Test in staging.
Day 3: ARM Migration
For each Node.js, Python, Go, Java, Ruby function:
- Check if dependencies support ARM
- Update IaC:
Architectures: [arm64] - Deploy to staging, validate, then production
Day 4: Provisioned Concurrency + Timeouts
- Audit Provisioned Concurrency usage in CloudWatch (utilization metric)
- Remove or reduce on functions with under 50% utilization
- Cap timeouts on functions with excessive headroom
Day 5: Network and Logging
- Enable VPC Endpoints for AWS services (free for S3/DynamoDB)
- Reduce log verbosity to INFO in production
- Audit retry logic for missing exponential backoff
- Document all changes and lock in the new baseline
After the 5-day audit, monitor for 30 days. The cost reduction shows up immediately on the next bill.
When To NOT Optimize Lambda (And Use Something Else Instead)
Lambda is not always the right answer. If you're running into Lambda's limits, the right answer is often a different compute option, not more Lambda tuning.
Switch to Fargate or ECS when:
- A single function runs hot 12+ hours/day (sustained traffic)
- You need over 10GB memory
- You need execution time over 15 minutes (Lambda's hard cap)
- You need persistent network connections (WebSocket servers, long-lived workers)
Switch to Cloud Run when:
- You're considering moving the workload off AWS anyway
- You need higher concurrency per container (Lambda is 1 request per container; Cloud Run is up to 1000)
- You want true serverless without per-invocation pricing
Stay on Lambda when:
- Workload is genuinely event-driven (S3, DynamoDB Streams, SQS, EventBridge)
- Sporadic traffic (under 100 invocations/sec)
- Tight AWS ecosystem integration (API Gateway, AppSync, etc.)
- Short execution times (under 30 seconds)
For workloads that fit Lambda well, the 12 patterns above cut costs 50-85%. For workloads that don't fit Lambda well, use the Cloud Run vs Fargate vs Lambda decision framework instead.
The Bottom Line
The average Lambda bill in 2026 is 60% higher than it should be due to a small set of recurring waste patterns. Memory over-allocation alone accounts for 25-50% of typical waste. Missing ARM migration adds another 20%. Unused Provisioned Concurrency and excessive timeouts compound the bill further. None of these require application code changes — they're configuration and IaC fixes.
The discipline most teams skip: treating Lambda configuration as a continuous optimization, not a one-time deployment decision. Memory needs change as code evolves. Architectures (ARM) become available. Runtimes deprecate. Provisioned Concurrency that made sense 6 months ago doesn't today. Audit Lambda costs every quarter.
If your Lambda bill is over $10,000/month and you haven't audited memory and architecture in the last 6 months, you are very likely overpaying by 50%+. Our cloud cost optimization team runs free Lambda audits and typically captures 50-70% savings within 1 week. Run a free Cloud Waste Scorecard to find your biggest serverless cost leaks first.
Further reading:
- AWS Lambda Pricing Deep Dive 2026
- AWS Lambda vs Fargate Cost Breakeven 2026
- Cloud Run vs Fargate vs Lambda Serverless Decision 2026
- AWS Network Cost: NAT Gateway vs VPC Endpoints vs PrivateLink
- Serverless Cost Optimization Autoscaling Guide 2026
- Spot Instances Decision Framework 2026
- Cloud Cost Optimization FinOps Service
- AWS Lambda Power Tuning Tool
- AWS Lambda Pricing



