Why your serverless costs are higher than they should be
28 June 2026 · 6 min read
Serverless is supposed to be the cost-efficient choice. Pay per invocation, no idle servers, scale to zero. The pitch is compelling, and at low scale it holds up — your first few months of Lambda invocations might cost less than a coffee.
Then your product gets traction. Traffic grows. The bill arrives. And somewhere between the DynamoDB line item and the API Gateway charges, you realise the mental model you used to justify the architecture is no longer matching the numbers.
This isn’t a failure of serverless. It’s a failure of cost visibility. Serverless billing is granular in a way that makes it easy to underestimate at scale. Here’s where the money actually goes, and what to do about it.
The memory misconfiguration tax on Lambda
Lambda pricing has two components: the number of invocations and the duration × memory. Most teams configure memory once during development and never revisit it. The default is 128 MB. Most functions run fine on it. Some run slowly.
Here’s the trap: Lambda allocates CPU proportionally to memory. A function that takes 800ms at 128 MB might take 200ms at 512 MB — and because duration billing is in 1ms increments, the faster execution can be cheaper despite the higher memory setting. AWS calls this the price-performance sweet spot, and finding it is the single highest-leverage cost optimisation available for Lambda.
The tool for this is AWS Lambda Power Tuning — an open-source Step Functions state machine that runs your function at multiple memory configurations and plots cost vs. performance. Run it against your five highest-invocation functions. The results are consistently surprising.
A second trap: functions that are over-provisioned for memory to avoid timeouts. If your function regularly uses 90 MB but is configured for 1024 MB, you’re paying for 934 MB you’re not using. CloudWatch Logs REPORT lines include Max Memory Used — set up a metric filter and alert when average utilisation drops below 50%.
DynamoDB: when on-demand becomes a liability
DynamoDB on-demand is the natural choice when you’re starting out. No capacity planning, no cold start cost, scales automatically. At low traffic it’s unambiguously correct.
At sustained, predictable load it becomes expensive. On-demand charges per read and write request unit. Provisioned capacity charges per hour regardless of usage, but at a significantly lower rate per request at consistent throughput. The crossover point is roughly 20% utilisation — if your table is busy more than 20% of the time, provisioned is cheaper.
The mistake teams make is assuming on-demand is always “safer.” It is operationally safer. It is not financially safer at scale. And it’s invisible until you look at Cost Explorer filtered by service → DynamoDB, grouped by usage type.
Three other DynamoDB cost traps worth checking:
Scans. A Scan operation reads every item in the table and charges for each read. A table with 10 million items is 10 million read request units per scan, regardless of how many items match your filter. If you see scans in your access patterns, you need a GSI or a query redesign.
GSI over-replication. Each Global Secondary Index is billed separately for its write capacity — every write to the base table is replicated to every GSI. A table with five GSIs pays six times the write cost of the same table with one. Audit your GSIs for read access frequency; remove the ones nobody queries.
Large items. DynamoDB billing rounds up to the nearest 1 KB per read/write. An item that’s 1.1 KB costs two read units to retrieve. Keeping items lean — storing large blobs in S3 and referencing them — can halve your read costs on attribute-heavy workloads.
API Gateway: the per-request cost you stop noticing
API Gateway REST APIs charge $3.50 per million API calls plus data transfer. That sounds negligible until you’re handling 50 million requests per month and the line item is $175 before data transfer — every month, forever, for infrastructure that adds latency and doesn’t cache anything.
Three places to look:
REST vs HTTP APIs. HTTP APIs cost roughly 70% less than REST APIs ($1.00 vs $3.50 per million). If you’re not using REST-specific features (request validation, usage plans, API keys, per-stage throttling), migrate to HTTP API. This is usually a low-risk change with significant savings.
Caching. API Gateway supports response caching at the stage level. For GET endpoints with stable responses, a cache TTL of 60 seconds can absorb the majority of traffic at zero additional Lambda invocation cost. The cache itself has an hourly charge, but at high request volume the Lambda savings dominate.
Redundant invocations. Look for endpoints that Lambda calls other AWS services to satisfy — another Lambda, an SSM parameter, a Secrets Manager secret. Each of those has its own invocation or API call cost. Secrets Manager charges $0.05 per 10,000 API calls; at high Lambda concurrency, fetching secrets per-invocation rather than caching them at initialisation can add up meaningfully.
Reading your bill with Cost Explorer
The raw bill is not useful. Cost Explorer with the right filters is.
Start with: Service → Lambda, grouped by Usage Type. You’re looking for Lambda-Duration and Lambda-Requests as the two dominant line items. If Lambda-Duration dwarfs Lambda-Requests, your functions are long-running — candidates for timeout review and memory tuning. If Lambda-Requests is high, you have a high-invocation function that may benefit from batching.
Then: Service → DynamoDB, grouped by Usage Type. DynamoDB:ReadRequestUnits and DynamoDB:WriteRequestUnits are the key lines. A write-heavy workload with GSIs will show replicated write costs — the number of write units will be a multiple of your actual application writes.
Finally: filter by Tag if you tag your resources by service or team. Cost without attribution is noise. If you can answer “what does the user search feature cost per month?” you can make architecture decisions with financial grounding. If you can’t, start tagging.
Five fixes that move the needle
The list of possible serverless cost optimisations is long. These five have the highest return on engineering time:
1. Run Lambda Power Tuning on your top-cost functions. One afternoon of work, measurable results the next billing cycle.
2. Switch on-demand DynamoDB tables to provisioned if utilisation is consistent. Use Application Auto Scaling to handle traffic spikes without manual capacity management.
3. Migrate REST API Gateway to HTTP API where REST-specific features aren’t needed. Immediate 70% reduction in API Gateway costs with minimal code change.
4. Cache secrets and configuration at Lambda initialisation, not per-invocation. Move SSM Parameter Store and Secrets Manager calls outside the handler function body. They run once per container lifecycle, not once per request.
5. Add Cost Allocation Tags and review weekly. Costs that aren’t attributed don’t get fixed. Tags are the prerequisite for every other optimisation.
The pattern underneath the fixes
Serverless costs are invisible by design. You don’t get a server bill. You get a request bill, a duration bill, a read unit bill, a write unit bill — fragmented across services, compounding at scale. The teams that manage this well aren’t the ones who chose a cheaper architecture. They’re the ones who made cost visibility a first-class engineering concern from early on.
The Well-Architected Cost Optimisation pillar has a principle worth internalising: expenditure awareness. It doesn’t mean being cheap. It means knowing what you’re spending, on what, and whether the value justifies it. For serverless workloads, that awareness starts with a Cost Explorer session and ends with a set of tagged, tuned, right-sized resources.
That’s the work. It’s not glamorous. It’s also the work that keeps the architecture defensible as the product scales.