What it costs to run a SaaS on Lambda and one DynamoDB table
I run a small SaaS on AWS with no servers: a few Lambda functions, one DynamoDB table and a CDN. People often say this kind of setup costs "about nothing" until it suddenly doesn't, so here's the real bill for October, the month it launched, broken down by service, plus what I think changes as it grows.
The numbers come from AWS Cost Explorer, filtered to a tag that every resource for this product carries. (The AWS account also runs another project, so the account total would be the wrong number. Small lesson in itself: AWS only splits costs by a tag after you activate it as a "cost allocation tag" in the billing settings. Mine wasn't, so my first query came back with $0.00 and I had to backfill it.)
The setup
- Website and widget: static files in
S3, served byCloudFront. The marketing pages are prerendered HTML, so crawlers don't need to run JavaScript. - API:
API Gateway(the cheaper HTTP API type) in front of one Node.jsLambdafor everything, plus a second smallLambdafor Stripe webhooks. Both run onarm64(Graviton) with 512 MB of memory. - Database: one
DynamoDBtable in on-demand mode, with every kind of record (boards, ideas, votes, accounts) in the same table under different keys. No secondary indexes. - Logins:
Cognito, for the people who own boards. Visitors who vote never log in. - Email:
SES, for "someone suggested an idea" alerts. - AI: Claude Haiku on Amazon Bedrock, for an optional summary that groups a board's ideas into themes. It only runs when an owner clicks the button.
- Everything else: a
Route 53hosted zone, oneKMSkey, and access logs kept for 30 days. No VPC, no NAT gateway, no RDS, no containers.
October's bill
| Service | October |
|---|---|
| Route 53 (DNS) | $0.51 |
| KMS (encryption key) | $0.15 |
| S3 (storage and requests) | $0.08 |
| API Gateway | $0.0014 |
| DynamoDB | $0.0004 |
| Lambda | $0.00 |
| CloudFront | $0.00 |
| Cognito | $0.00 |
| CloudWatch | $0.00 |
| Total | $0.74 |
Seventy-four cents. And almost all of it is the part that doesn't depend on traffic: the DNS zone ($0.50) and the encryption key ($0.15) are 88% of the bill.
The parts people usually worry about came to almost nothing. Lambda ran 1,223 times and used 192 GB-seconds of compute, all inside the free tier (1 million requests and 400,000 GB-seconds a month). CloudFront served about 38,000 requests and 0.23 GB, also free. DynamoDB handled 764 read units and 376 write units for less than a twentieth of a cent.
The biggest line that does move with use is S3, at 8 cents, and it's almost all requests: 8,591 writes and 77,233 reads. Visitors don't explain that many; most of it is probably me, uploading files on every deploy and downloading the access logs every time I run my traffic report. My own tooling cost more than my users did.
Two things don't appear under the tag at all. Email (SES) has no line, so the few notification emails I sent cost nothing I can see. And the AI summaries: October had exactly one, my own test. Model calls on Bedrock aren't tagged resources unless you set up an application inference profile, which I haven't, so even that one call doesn't show in the tagged bill. With one call there's nothing to report yet; the budget alarm described below is there for when there is.
Fixed costs vs pay-per-use
Almost every line above scales with use: no traffic, no charge. A few don't:
- The Route 53 hosted zone is $0.50 a month per domain, whether anyone visits or not. The DNS queries themselves (about 24,000 in October) added one cent.
- The KMS key is $1 a month per key, charged by the day. I created mine late in October, to encrypt access tokens for a Webflow integration, so October only paid 15 cents of it. From November it's the full dollar, which will make it the biggest line on the bill.
- The domain itself is paid yearly to the registrar, outside AWS.
At launch, the costs that don't depend on traffic are the bill. That will stay true for a long time.
Where the traffic actually went
Recently I wrote about my launch-week logs showing 749 page views when the real number was 31; the rest was bots. That matters for cost too, but less than I expected:
- Page requests from bots hit CloudFront and S3, which serve cached static files. They're the cheapest requests in the whole system.
- The board API isn't cached, deliberately: votes need to show up straight away. So every time a widget loads a board, that's one API Gateway request, one Lambda run and one DynamoDB query.
This is the part that scales with real use. In October that was 1,223 API requests, from visitors' widgets and from me in the dashboard. API Gateway charged $0.0014 for them and DynamoDB $0.0004; Lambda was free. That's about $1.50 per million API calls on the bill, plus Lambda once the free tier runs out.
Decisions that keep it cheap
None of these are clever. They mostly avoid the well-known ways a "serverless" bill surprises people:
- No VPC. Lambda functions inside a VPC that need the internet usually need a NAT gateway, which costs money every hour whether or not anything runs: $0.045 an hour in US East (Ohio), over $30 a month for one gateway before any data goes through it. That's about 40 times my whole October bill. Mine don't need a VPC, so there's nothing billing by the hour.
- One table, on demand, no indexes. On-demand billing means no capacity to pay for while idle. Every secondary index would be another copy of each write, so I designed the keys to answer every query without one.
- arm64 Lambdas. Graviton is priced lower per GB-second than x86 for the same memory, and Node.js runs on it without changes. It made no difference in October, because everything fit in the free tier, but it will once the free tier runs out.
- Logs expire. Lambda logs and CloudFront access logs are kept for 30 days, then deleted. Log storage is a slow-growing cost that nobody notices until it isn't small.
- A ceiling on abuse. API Gateway is throttled at 50 requests a second (bursts to 100). That's far more than real use needs, and it caps what a script hammering the API can run up.
- AI only on request, with limits and an alarm. The summary runs when an owner clicks the button: once a day on the free plan, ten on the paid plan. An AWS Budget of $10 a month on the model emails me when actual spend passes half of it, or when AWS forecasts the month will go over. A per-call AI feature is the one part of this stack where a bug or an abuser could actually cost real money.
What changes as it grows
A rough estimate from October's bill, not a forecast. Assume each paying board gets 1,000 API calls a month (widget loads, votes, suggestions), and each call costs what October's did: one API Gateway request, about 0.16 GB-seconds of Lambda, and a little DynamoDB.
- 100 boards: 100,000 calls. About $0.15 for API Gateway and DynamoDB, Lambda still free. The bill stays under $2 a month, nearly all of it fixed costs.
- 1,000 boards: 1 million calls. About $1.50 for API Gateway and DynamoDB, and Lambda right at the edge of the free tier (1 million requests, about 160,000 of the 400,000 free GB-seconds). Note the free tier is per AWS account, so anything else in the same account uses it up too. Call it a few dollars a month, against 1,000 paying customers.
What I expect, beyond the sums:
Fixed costs stay flat. One hosted zone and one key cost the same at 1,000 customers as at zero. Board loads become the main cost: Lambda, API Gateway and DynamoDB reads, all per request. If that ever gets expensive, the obvious fix is a short cache for board reads. AI summaries are the wild card. Per-click model costs are small, but they're the only line that grows with how much people click rather than how much traffic they get. Email grows with the number of ideas submitted, at SES's price of $0.10 per 1,000 emails.
What this doesn't count
The AWS bill isn't the cost of the business. Not included above: the domain, the email provider for my own inbox, Stripe's fees on each payment, and my time, which is by far the biggest cost and the hardest to put a number on.
The short version
The product cost $0.74 to run in its first month, and 88% of that was a DNS zone and an encryption key that would have cost the same with zero visitors. Serverless was the right call for a product that might get ten visitors a day or ten thousand: I pay for what's used, and the bill at zero is close to zero. The things worth doing on day one are cheap and boring: no VPC unless you need one, logs that expire, a throttle on the API, and a budget alarm on anything billed per AI call.
What's the line on your AWS bill that surprised you, in either direction?
Disclosure: the product is UserNote, a feedback board and public roadmap for websites, which I build and run on my own.
Comments
No comments yet. Start the discussion.