A Series B SaaS company came to us after their CFO flagged cloud costs as a top-three burn concern heading into Q3. They were spending $52K/month on AWS. Their CTO's honest assessment: "I know it's too high, but every time we look at it, we don't know where to start."
Two weeks of cost auditing and four weeks of targeted changes later, their bill was $19K/month. Not through heroics or architecture rewrites — through a methodical audit of the places startups almost always overpay, applied in order of impact.
What the audit found
We use AWS Cost Explorer, Trusted Advisor, and Infracost as starting points, but the real work is cross-referencing billing data against actual utilization. Here's what we found:
- Oversized EC2 instances (−$9,200/mo): Their production EKS node group was running m5.4xlarge instances at 18% average CPU and 31% average memory. Actual workload fit comfortably on m5.xlarge. The original sizing had been "temporary" pending a performance test that never happened — two years ago.
- Idle and orphaned resources (−$4,800/mo): Seven RDS snapshots of decommissioned databases, four NAT gateways in regions with no active workloads, two load balancers with no registered targets, and an ElasticSearch cluster running at 3% utilization for a feature that had been removed from the product six months earlier.
- NAT gateway data processing (−$6,100/mo): Their application pods were pulling container images and fetching S3 objects through the NAT gateway instead of using VPC endpoints. NAT gateway charges $0.045/GB of processed data. At their traffic volume, S3 and ECR VPC endpoints paid for themselves in the first day.
- No Savings Plans or Reserved Instances (−$8,400/mo): Every instance, RDS database, and ElastiCache cluster was on-demand. For a two-year-old company with stable baseline infrastructure, this was the largest single savings opportunity. We analyzed their trailing 3-month baseline usage and bought Compute Savings Plans covering 70% of it.
- S3 storage class misuse (−$3,100/mo): 14TB of objects in S3 Standard that hadn't been accessed in over 90 days — old build artifacts, log archives, and user-uploaded files from a deprecated feature. S3 Intelligent-Tiering and a lifecycle policy to Glacier Instant Retrieval for objects older than 30 days fixed this without touching any application code.
- CloudWatch Logs retention (−$1,400/mo): Log groups with no retention policy set, accumulating indefinitely. Application logs from 2023 still stored at full price. Setting a 30-day retention policy on application logs and a 1-year policy on audit/security logs immediately reduced storage by 80%.
The changes, in order of effort vs. impact
Week 1: Zero-risk wins
We started with changes that had no application impact and could be reversed in minutes. Deleting orphaned resources, setting CloudWatch retention policies, enabling S3 lifecycle rules, and creating VPC endpoints for S3 and ECR took two days. That alone was $11K/month off the bill before we touched a single instance or database.
Week 2: Savings Plans
Savings Plans are a commitment, so we spent time analyzing the right coverage level before buying. The goal is to cover your stable baseline — the compute you'll definitely be running in a year — without over-committing and leaving yourself unable to scale down. We landed on a 1-year Compute Savings Plan at 70% baseline coverage. Break-even: 18 days. Ongoing savings: $8,400/month.
Weeks 3–4: Right-sizing
Right-sizing is the most operationally complex change. You need utilization data over a meaningful time window (we use 30 days minimum), a clear plan for each instance type change, and a rollout that doesn't surprise the application team.
Our approach:
- Pull CloudWatch metrics for CPU, memory (via the CloudWatch agent), and network for every instance and RDS database
- Identify candidates with <40% average utilization across all three metrics
- Test the new instance type in staging first — let it run for 3 days under normal traffic patterns
- Apply to production with a 1-hour monitoring window before calling it done
We right-sized 14 EC2 instances, 4 RDS instances, and 2 ElastiCache clusters. Every change was done without downtime using instance modifications during a low-traffic window or rolling node group replacements in EKS.
What we didn't touch
Cost optimization done wrong breaks things. We explicitly avoided:
- Spot instances for stateful workloads. The savings are real, but a Spot interruption at 3am on an RDS migration or a Kafka broker isn't worth it. Spot makes sense for batch processing and stateless workers — not production databases or queue consumers.
- Aggressive S3 archival. Glacier Instant Retrieval is cheap, but retrieval costs add up if your application unexpectedly starts accessing archived objects. We were conservative: only applied lifecycle rules to data we could confirm was genuinely cold.
- Cutting monitoring coverage to save on CloudWatch costs. Two clients in the past have done this. Both had incidents they couldn't diagnose because the metrics they needed weren't there. The ~$800/month they saved cost them far more.
The results
- Monthly bill: $52,000 → $19,000 (63% reduction)
- Annualized savings: $396,000
- Implementation time: 4 weeks
- Application downtime: Zero
- Incidents caused: Zero
We also set up AWS Cost Anomaly Detection with Slack alerting, so any future cost spike triggers an alert within 24 hours — before it compounds into a quarterly problem. The CFO now gets a weekly cost report from Cost Explorer. The CTO no longer gets surprised at the end of the month.
"We knew we were overpaying but couldn't prioritize the investigation. In four weeks, they paid for themselves many times over — and now I actually understand what we're spending and why."
The pattern
Every startup we've audited has some version of the same issues: instances sized for a load test that never materialized, orphaned resources nobody deleted, and on-demand pricing on infrastructure that hasn't changed in a year. The savings aren't exotic — they don't require a new architecture or a risky migration. They require looking.
The reason most teams don't do this isn't that it's hard. It's that it's not urgent — until the CFO makes it urgent. We run a cost audit as part of every new engagement onboarding because fixing costs in week one funds the rest of the engagement.