Cloud Cost Anomaly Detection
The Slack message arrived on a Tuesday: "Why is our AWS bill $47k this week?" The answer was a staging EKS cluster that autoscaled to 847 nodes over the weekend because someone left a load test script running with while true. Finance found out from the invoice. Engineering found out from Finance. That's the workflow cost anomaly detection exists to break.
What anomaly detection actually does
Compare current spend (hourly or daily) against an expected baseline. Flag deviations beyond a threshold. Route alerts to the team owning the resource via tags.
It's not budgeting — budgets tell you you're over plan at month end. Anomaly detection tells you today that RDS spend tripled in us-east-1.
Daily spend by service
│
$$$ │ ╱╲ ← alert fires here
│ ╱╲ ╱ ╲
$$ │ ╱╲ ╱ ╲╱ ╲
│───╱──╲╱────────────── baseline (7-day avg)
$ │
└────────────────────────► time
Tagging is the prerequisite
Anomaly alerts without team, env, and service tags are useless. You'll know spend spiked; you won't know whether to page platform or ML.
Enforce tags at creation with AWS Service Control Policies, Terraform default_tags, or OPA Gatekeeper:
provider "aws" {
default_tags {
tags = {
Environment = var.environment
Team = var.team
Service = var.service
CostCenter = var.cost_center
}
}
}
Untagged resources should trigger their own weekly report — we auto-page if untagged spend exceeds 5% of total.
AWS Cost Anomaly Detection
Native option for AWS-heavy shops. Enable in Billing console or via API:
aws ce create-anomaly-monitor \
--anomaly-monitor '{
"MonitorName": "service-level",
"MonitorType": "DIMENSIONAL",
"MonitorDimension": "SERVICE"
}'
aws ce create-anomaly-subscription \
--anomaly-subscription '{
"SubscriptionName": "platform-alerts",
"MonitorArnList": ["arn:aws:ce:..."],
"Subscribers": [{"Address": "[email protected]", "Type": "EMAIL"}],
"Threshold": 100.0,
"Frequency": "DAILY"
}'
Monitors can be dimensional (by service, linked account) or custom (by tag). Subscriptions set dollar thresholds and notification targets. AWS uses ML internally — reasonable defaults for accounts with 14+ days of history.
Limitations: AWS-only, daily granularity for some alerts, and tag-based monitors need consistent tag coverage.
Build-your-own with CUR and Prometheus
For multi-cloud or finer control, export Cost and Usage Reports to S3, aggregate with Athena or DuckDB, push metrics to Prometheus/Grafana:
-- Athena: daily spend by service, last 14 days
SELECT
line_item_product_code AS service,
DATE(line_item_usage_start_date) AS day,
SUM(line_item_unblended_cost) AS cost
FROM cur_database.cur_table
WHERE line_item_usage_start_date >= CURRENT_DATE - INTERVAL '14' DAY
GROUP BY 1, 2
ORDER BY 2, 3 DESC
Alert rule (Prometheus-style pseudocode):
- alert: CloudCostAnomaly
expr: |
(
sum by (service) (cloud_daily_cost)
/
sum by (service) (cloud_daily_cost offset 7d)
) > 1.5
and sum by (service) (cloud_daily_cost) > 200
for: 1h
labels:
severity: warning
The offset 7d comparison catches week-over-week spikes while ignoring normal weekday/weekend cycles. Add offset 1d for faster detection on flat baselines.
Reducing alert fatigue
Raw anomaly detection pages too much. Layer these:
Allowlists for known spikes. Month-end reporting jobs, Black Friday pre-scale, quarterly penetration test environments. Maintain a calendar feed that suppresses alerts.
Progressive severity. 30% increase → Slack #finops. 100% increase or >$5k → PagerDuty.
Root-cause enrichment. Link alerts to Cost Explorer filtered views. Our webhook attaches top 5 resource IDs by cost delta — cuts investigation from 40 minutes to 5.
Hourly vs daily. Enable hourly monitors for expensive services (EC2, RDS, SageMaker). Daily is fine for S3 storage creep.
Operational response playbook
When an alert fires:
- Identify service and linked account from the alert
- Open Cost Explorer → Group by Resource → Sort by cost change
- Check recent deploys, autoscaling events, and CI pipelines in that window
- Terminate or scale down offending resources
- Post incident summary in
#finopswith$ impactandprevention action
We added a max_nodes cap on Karpenter NodePools after the 847-node incident. Anomaly detection found it; the cap prevents recurrence.
Integrating anomaly alerts with incident response
Wire cost anomaly webhooks into the same Slack channel as infrastructure incidents but with a distinct emoji and runbook link. On-call should not confuse a $2k RDS spike with a checkout outage — different severity, different first responder (FinOps vs product on-call).
Build a weekly cost review ritual: top ten services by spend delta, untagged resource report, and reserved instance coverage. Anomaly detection catches spikes; weekly review catches slow leaks — S3 lifecycle missing, old snapshots accumulating, dev clusters left running over weekends.
Export anomaly events to your data warehouse. Correlating cost spikes with deploy timestamps and autoscaling events in one SQL query beats clicking between AWS console tabs during an investigation.
Common production mistakes
Teams get cost anomaly detection wrong in predictable ways:
- Skipping failure-mode rehearsal — run a game day or fault injection exercise before peak traffic, not after the first outage.
- Missing correlation context — every error path should carry request, trace, or tenant identifiers so incidents are debuggable.
- Optimizing for demo, not steady state — load tests, cache warm-up, and cold-start paths matter more than local dev latency.
- Undocumented trade-offs — if you chose speed over strict correctness (or vice versa), write that down for the next engineer.
Production implementations of cost anomaly detection fail when staging mirrors production topology poorly, rollback is untested, and on-call runbooks describe the happy path only.
Debugging and triage workflow
When cost anomaly detection misbehaves in production, work top-down instead of guessing:
- Confirm scope — one tenant, region, or deployment stage? Narrow blast radius before deep diving.
- Check recent changes — deploys, flag flips, config pushes, and schema migrations in the last 24 hours.
- Compare golden signals — latency, error rate, saturation, and traffic for the affected surface vs. baseline.
- Reproduce minimally — smallest input or scenario that triggers the failure; capture traces/logs with correlation IDs.
- Fix forward or rollback — if rollback is faster than root-cause during incident, rollback first, postmortem second.
- Add a guard — alert, integration test, or circuit breaker so the same class of failure is caught earlier next time.
Document the timeline during triage. Future you (and on-call) will need timestamps, not just conclusions.
Resources
- AWS Cost Anomaly Detection documentation
- FinOps Foundation anomaly detection guide
- AWS Cost and Usage Reports setup
- Datadog Cloud Cost Management
- Infracost — shift-left cost estimation
Frequently asked questions
What causes most unexpected cloud cost spikes?
Runaway autoscaling (infinite loop spawning instances), forgotten load test environments, misconfigured data transfer (cross-region egress), orphaned EBS volumes and snapshots, and GPU instances left running after ML experiments. Anomaly detection catches these; tagging tells you who to page.
How much of a cost increase should trigger an alert?
Use relative and absolute thresholds together: alert on >30% day-over-day increase AND >$500 absolute impact for your scale. Small percentages on tiny baselines create noise; large absolute jumps on predictable monthly cycles (month-end batch jobs) need allowlists.
Do you need ML for cost anomaly detection?
No for most teams. AWS Cost Anomaly Detection, Datadog Cloud Cost Management, and simple statistical baselines (7-day rolling average + 2σ) catch 90% of incidents. ML helps with seasonal businesses where spend legitimately varies in complex patterns.
Hiring a senior Android / Flutter engineer?
I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.
Get in touch →