I built a cheaper alternative to AWS Cost Anomaly Detection
A NAT Gateway I thought I'd deleted was still running. I found out on the 1st of the month when the invoice came in. Thirty-one days of waste.
I'd set billing alerts — the kind that fire when your total monthly spend crosses a threshold. But by the time a threshold alert fires, you've already accumulated the spend. And the alert tells you your total is high, not which service started the leak.
So I built CostWatch.
What it does
Every day at 6 AM UTC, CostWatch pulls Cost Explorer data for every connected AWS account. For each service, it compares yesterday's spend against the 7-day rolling average. If any service jumps more than 50% day-over-day and more than $5 in absolute terms, you get an email:
CostWatch anomaly alert — production-account
Yesterday's spend had unexpected spikes:
Amazon EC2: $38.40 yesterday (7-day avg: $8.20, +368%)
Amazon S3: $14.90 yesterday (7-day avg: $1.10, +1254%)
✓ All other services normal
Review: console.aws.amazon.com/cost-management/home#/cost-explorer
That's it. One email. Which service, how much, how far from normal. You can be reading Cost Explorer in 60 seconds.
Every Monday morning there's a weekly digest: total this week vs last week, top 5 services by spend, and days-remaining-at-current-burn if you've set a budget.
Why not just use AWS Cost Anomaly Detection?
AWS launched Cost Anomaly Detection a few years ago. It's a reasonable product. But it has some friction:
Cost. AWS charges $0.000008 per dollar of spend that it evaluates. There's also a minimum evaluation charge that works out to roughly $8/mo for most accounts. CostWatch is $5/mo.
Alert format. AWS sends you a link to a Cost Explorer report. The report has multiple tabs, groupings, and time-range selectors. If you're someone who uses Cost Explorer regularly, this is fine. If you're an indie developer with an AWS account you mostly ignore until something goes wrong, navigating Cost Explorer is a context switch.
Setup. You configure Cost Anomaly Detection inside Cost Explorer's UI, which means you have to be in Cost Explorer to do it. CostWatch setup is one CloudFormation snippet and a role ARN. Five minutes.
ML model. AWS uses an ML model to detect anomalies. This has real advantages for complex accounts with seasonal patterns. It also means alerts can be noisy or miss obvious spikes because the model is working from patterns, not thresholds. CostWatch uses a simple comparison: if yesterday was more than 50% above the 7-day average and more than $5 absolute, it's an anomaly. Transparent, predictable, adjustable in the self-hosted version.
The architecture
Simple:
EventBridge (daily, 06:00 UTC)
→ CostScannerLambda
→ STS AssumeRole (cross-account, read-only)
→ GetCostAndUsage (DAILY granularity, grouped by SERVICE)
→ Compare: yesterday vs rolling 7-day average
→ Queue anomalies → DynamoDB
→ If Solo/Team plan: send immediate email via SES
EventBridge (weekly, Monday 08:00 UTC)
→ WeeklyDigestLambda
→ Pull pending anomalies from DynamoDB
→ GetCostAndUsage (this week vs last week)
→ Send plain-text digest via SES
→ Clear pending queue
API Lambda (HTTP API Gateway)
→ Magic-link auth → dashboard → Stripe Checkout
Five DynamoDB tables. Two cron Lambdas. One API Lambda. SAM template for one-command deploy.
The cross-account IAM model is the interesting security part. CostWatch never stores your credentials. Instead, you deploy a CloudFormation template in your account that creates a read-only IAM role (AWSBillingReadOnlyAccess only — no EC2 describe, no S3 list, nothing else). CostWatch assumes that role using STS with a per-user ExternalId, which prevents a confused deputy attack where a compromised CostWatch could pivot to other users' accounts.
The $5 absolute threshold
One design decision worth explaining: why require both a percentage spike and an absolute dollar amount?
Without the absolute threshold, you'd get alerts when VPC Flow Logs go from $0.01/day to $0.03/day. That's a 200% spike. It's also $0.02 of additional spend. Not worth an email.
The $5 threshold means a service has to have moved at least $5 day-over-day before you hear about it. At $8/day average EC2 spend, a 50% spike is $4 — barely worth the email. At $40/day average, a 50% spike is $20 — definitely worth knowing.
This is adjustable in the self-hosted version. I might expose it in the Team plan UI eventually.
Pricing
- Free (self-hosted): Deploy the SAM template to your own account. You run it, you pay your own AWS costs (~$0.10/mo in Lambda + SES). No SaaS, no dashboard.
- Solo ($5/mo): One AWS account, daily anomaly email, weekly digest, dashboard.
- Team ($12/mo): Up to 5 AWS accounts, same features, one combined digest.
The self-hosted option is important. HN credibility matters for distribution — "you can run this yourself for free" is a different conversation than "trust me with your cloud access."
The deploy path
One CloudFormation snippet in your AWS console. It creates the IAM role. You copy the ARN. You paste it in the CostWatch dashboard. That's the entire setup.
# CloudFormation — paste in your account
Resources:
CostWatchRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
AWS: !Ref CostWatchAccountId
Condition:
StringEquals:
sts:ExternalId: !Sub "cw-${UserId}"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSBillingReadOnlyAccess
Five minutes from zero to first scan.
Status
CostWatch is working code (SAM template, cron Lambdas, API Lambda, Stripe webhook, dashboard). It needs a domain (costwatch.app), SES identity verification, and Stripe product setup before it's live. If you want to self-host, the SAM template is in the GitHub repo.
The target user is anyone who's had a "wait, why is my AWS bill this high" conversation — and wants to know the next day, not the first of next month.