72% Cloud Cost Reduction: A FinOps Playbook for SaaS
A monolithic ECS service was running on six on-demand Fargate tasks per region, 24/7, across three regions. The bill hit $41.6K/mo. Traffic was spiky but predictable — yet the autoscaler was set to on-demand only because nobody trusted spot for stateful workloads.
The diagnosis
The first thing I did was pull 90 days of CloudWatch metrics and plot actual CPU/memory utilization against provisioned capacity. The result: average utilization was 14%. The other 86% was paying for idle.
The second thing: I looked at the traffic pattern. It was spiky but predictable — peaks at 9am and 2pm local time, troughs overnight. The autoscaler was treating every spike as an emergency and never scaling down fast enough.
The fix: split, shift, and schedule
**Split** the monolith into twelve stateless services behind Caddy. Stateful bits (Postgres, Redis) stay on-demand; everything else can run on spot.
**Shift** non-critical workloads to whichever region has surplus spot capacity that hour. A Terraform-managed scheduler checks spot pricing across regions every 15 minutes and rebalances.
**Schedule** the autoscaler to pre-warm at 8:45am (before the 9am spike) and scale down aggressively at 6pm. The pre-warm eliminates the cold-start latency that justified the 24/7 on-demand baseline.
# Terraform: spot fleet with drain grace
resource "aws_spot_fleet_request" "stateless" {
iam_fleet_role = aws_iam_role.spot_fleet.arn
spot_price = "0.10"
target_capacity = var.target_capacity
allocation_strategy = "diversified"
instance_interruption_behavior = "terminate"
terminate_instances_with_expiration = true
launch_specification {
instance_type = "t3.medium"
ami = data.aws_ami.latest.id
spot_instance_metadata_options {
http_put_response_hop_limit = 1
}
}
}The CDN tuning nobody talks about
CDN tuning cut egress by another 18%. The trick: set `Cache-Control: public, max-age=31536000, immutable` on every hashed asset (JS/CSS bundles, images with content hashes in the filename). This lets the CDN cache them for a year — and more importantly, lets the browser cache them for a year.
Before: every page load fetched 2.3MB of JS from the origin. After: 180KB (just the changed chunks).
The payoff
- Bill dropped from $41.6K/mo to $11.6K/mo — **72% reduction**
- p99 latency fell 33% because the services could now scale independently — the old monolith's connection pool was the actual bottleneck, not CPU
- Zero customer-facing downtime during the migration
The lesson: most cloud bills aren't a pricing problem — they're an architecture problem. Fix the architecture, and the bill follows.