Your AWS bill was $3,000 a month last spring. It's $4,800 now. Nobody remembers launching anything that costs $1,800 a month, the product has roughly the same number of users, and there's no single line item big enough to explain the gap. That's the normal shape of an AWS cost problem: not one expensive mistake, but forty small ones compounding quietly across regions and accounts.
This guide is the practical version of AWS cost optimization — what actually drives the increase, which AWS tools cover which part of the problem and where they stop, a checklist you can work through in an afternoon, and how to keep the bill from drifting back up once you've cleaned it.
What does AWS cost optimization actually mean?
Strip away the framework language and there are three levers:
- Eliminate — stop paying for resources nothing uses.
- Right-size — pay for the capacity you actually consume, not what you guessed at launch.
- Commit — pay a lower rate for the baseline you know you'll keep, via Savings Plans or Reserved Instances.
Most cost-optimization advice starts at lever 3, because commitments are the easiest thing to pull from a finance seat: one purchase, an immediate percentage off, no engineering time. That's the wrong order, and it's expensive to get wrong. Levers 1 and 2 change what you run. Lever 3 only changes the price of whatever you happen to be running — including the waste, locked in for one to three years.
Why does an AWS bill grow when nobody changed anything?
Because AWS bills for what exists, not for what gets used. An EC2 instance at 2% CPU costs exactly what the same instance costs at 90% CPU. A load balancer with zero healthy targets bills the same hourly rate as one serving production traffic. An EBS volume attached to nothing bills the same per GB as your busiest database disk.
That single billing fact creates a ratchet. Every resource anyone creates adds a line item that runs forever until somebody deliberately deletes it — and deletion is nobody's job. A migration leaves the old database running "just for a week." A blue/green deploy leaves the old load balancer behind. A terminated instance leaves its volume, and the volume leaves a snapshot. Preview environments spin up per pull request and quietly outlive the pull request.
None of these is big enough to notice. A 100 GB unattached gp3 volume is about $8/month. An unassociated Elastic IP is about $3.60/month. An idle Application Load Balancer is about $22/month. Individually, rounding error. Forty of them across three regions and two accounts is most of that $1,800 gap.
Figures in this guide are typical US-region on-demand rates at the time of writing, rounded for clarity. Rates vary by region and change over time — confirm against the AWS pricing pages for your own region before you build a business case.
How do I see where the money is actually going?
Start in Cost Explorer, but don't stop at the default view. Grouping by service tells you "EC2 is expensive," which you already knew. The view that actually locates waste is group by Usage Type, because that's where the mechanism shows up: NatGateway-Bytes is data-processing fees, EBS:VolumeUsage.gp2 is storage sitting on the older volume type, LoadBalancerUsage is the flat hourly charge on balancers whether or not they route anything.
Set the date range to the last three months and switch the granularity to monthly. You're looking for line items that only go up, and for anything that started at zero and then became permanent.
The same breakdown is available from the CLI, which is easier to diff month over month:
# Top usage types by unblended cost, last full month
aws ce get-cost-and-usage \
--time-period Start=2026-06-01,End=2026-07-01 \
--granularity MONTHLY \
--metrics UnblendedCost \
--group-by Type=DIMENSION,Key=USAGE_TYPE \
--query 'ResultsByTime[0].Groups[?Metrics.UnblendedCost.Amount>`50`].[Keys[0],Metrics.UnblendedCost.Amount]' \
--output table
Two things to know before you lean on this. Cost Explorer data refreshes roughly once every 24 hours, so today's spend won't be there yet — it's a trend tool, not a live dashboard. And while the Cost Explorer console is free, each paginated API request costs $0.01. That's irrelevant for a monthly script and surprisingly relevant if you wire it into something that polls.
Which AWS cost optimization tools cover what?
AWS ships several free tools in this space, and they overlap enough to be confusing. Roughly:
| Tool | What it's good at | What it costs |
|---|---|---|
| Cost Explorer | Trends, usage-type breakdowns, forecasting, RI/Savings Plans coverage | Console free; API $0.01/request |
| AWS Compute Optimizer | Right-sizing and idle recommendations across EC2, EBS, Lambda, Fargate, RDS, ElastiCache, DynamoDB, NAT Gateway and more | Free; enhanced metrics extra |
| Trusted Advisor | A broad checklist including some idle-resource checks | Full checks need Business Support+ or Enterprise Support |
| Cost Optimization Hub | One place aggregating right-sizing, idle, Graviton and commitment recommendations | Free |
| Budgets + Cost Anomaly Detection | Telling you after spend moves unexpectedly | Free (Budgets charge only for automated actions) |
Cost Optimization Hub is the newest and the one worth enabling first — it's free, it consolidates recommendations across an Organization, and it puts estimated savings next to each one. Enable it from the Billing and Cost Management console in your payer account.
But understand the shape of what these tools do. They are right-sizing and commitment engines built around provisioned, metric-emitting resources. They're excellent at "this m5.2xlarge should be an m5.large" and "you could commit to $12/hour of compute," and their coverage keeps widening. What they have far less to say about is the storage-and-hygiene long tail: the snapshot of a volume of an instance you terminated last year, the CloudWatch log group with no retention policy quietly storing eight years of debug logs, the Elastic IP nobody released, the S3 bucket where everything sits in Standard because nobody ever wrote a lifecycle rule, the incomplete multipart uploads you're billed for and can't see in the console.
That long tail is also the part that comes back. Right-size an instance once and it stays right-sized. Delete every orphaned snapshot today and there will be new ones in six weeks.
An AWS cost optimization checklist you can run this week
Work top to bottom. This is ordered roughly by savings-per-minute for a typical small AWS account, and every item is safe to investigate read-only — the risk only starts when you delete.
- Unattached EBS volumes and orphaned snapshots. The single most common form of AWS waste, and the easiest to verify. Find and delete orphaned EBS volumes and snapshots.
- Idle EC2 instances. Low CPU alone is a bad signal — check network and disk activity too before you touch anything. How to find idle EC2 instances.
- Load balancers with no healthy targets. ~$22/month each, and they survive every teardown. How much does an idle load balancer cost?
- Unassociated Elastic IPs. Small individually, and since 2024 AWS charges for every public IPv4 address, attached or not. Why AWS charges for an Elastic IP you aren't using.
- NAT Gateway data processing. Often the largest single networking line item, and frequently fixable with VPC endpoints rather than architecture changes. Why is my NAT Gateway so expensive?
- S3 objects sitting in Standard forever. Cold data belongs in a cheaper class; the fix is a lifecycle rule, not a migration. S3 lifecycle rules for cold data.
- EFS file systems with no lifecycle policy. EFS Standard costs 13× S3 per GB-month, and cold data stays there forever unless a policy moves it — which it often silently fails to do. How to reduce your AWS EFS cost.
- Oversized RDS instances. Usually the biggest right-sizing win in the account, and safer to do than it sounds. How to right-size RDS without downtime.
- Idle ElastiCache and Redshift clusters. Both bill full price while doing nothing; Redshift can often be paused on a schedule instead of deleted. Idle ElastiCache clusters · Pausing idle Redshift clusters · what we flag as an idle warehouse.
- DynamoDB tables on provisioned capacity nobody consumes. Reserved throughput bills by the hour whether you use it or not, and provisioned only beats on-demand above ~29% utilization. DynamoDB on-demand vs provisioned cost.
- CloudWatch log groups with no retention policy. They default to storing logs forever, and storage is the cheap half — ingestion at $0.50/GB is usually the bigger number. Why is CloudWatch so expensive?
- gp2 volumes never migrated to gp3. gp3 is roughly 20% cheaper per GB with better baseline performance, and the change is live with no downtime.
Every item on this list reduces the baseline your commitment should be sized against. Clean first, measure the new steady state for a couple of weeks, then commit. Buying in the other order is how teams end up with three years of reserved capacity for instances they were about to delete.
Which optimizations are actually worth the engineering time?
The sequence matters more than any individual tactic.
Delete first. It's the only lever with no tradeoff. Nothing gets slower, no capacity planning is required, and the savings are permanent and immediate. It's also the least glamorous, which is exactly why it stays undone.
Right-size second. Now that you know what genuinely runs, match capacity to real utilization. This one needs judgement — a database at 30% CPU may have been sized for a quarterly batch job, not for its Tuesday average — so look at a 14-day window minimum, and at peaks rather than means.
Commit third. With a clean, correctly-sized baseline, Savings Plans and Reserved Instances become straightforward. A 1-year, no-upfront Compute Savings Plan typically takes a meaningful percentage off on-demand rates for the portion you cover; longer and all-upfront terms go deeper. Cover your floor, not your peak — you can always add another commitment, but you cannot un-buy one. Once the cleanup is done, Reserved Instance opportunity explains how we identify the steady-state footprint that is genuinely worth committing against.
Savings Plans and Reserved Instances are contracts. If you commit against a bloated baseline, you don't just fail to save — you actively lock in the waste for the full term, and every later cleanup reduces your coverage instead of your bill. Order matters here in a way it doesn't elsewhere.
Re-architect last, and selectively. Moving to Graviton, replacing an always-on service with Lambda, or consolidating NAT Gateways can all pay off well. They also cost real engineering weeks. Do them when the numbers justify it, not because they appear on a best-practices list.
How do you stop the bill creeping back up?
Cleanups decay. Here's what actually holds:
- Tag ownership, not just cost centres. An
ownertag turns "what is this?" — the question that stops most deletions — into a Slack message. Enforce it at creation with a Service Control Policy or in your IaC modules, because retroactive tagging never happens. - Set a budget with an alert. Budgets that only monitor and notify are free — you pay only if you attach automated actions. One alert at 110% of your normal monthly spend catches the big regressions.
- Turn on Cost Anomaly Detection. It's free, it learns your baseline, and it's the fastest signal for "someone left a GPU instance running over the weekend." Newer Cost Explorer accounts already have a default monitor, so the thing to check is whether anyone is actually subscribed to its alerts. How to set up AWS Cost Anomaly Detection covers the monitor and threshold choices, and the waste it can't see.
- Give the checklist a recurring slot. Monthly, on a calendar, owned by a name. Waste accumulates continuously, so a one-off audit has a half-life measured in weeks.
That last point is the honest limitation of doing all of this by hand. The checklist above is maybe an afternoon of work the first time. It is also an afternoon of work every month, across every region and every account, forever — and it's exactly the kind of task that survives two months of good intentions before quietly falling off the list.
How do I find all of this across my whole AWS account?
That's the gap Parsivex fills. It connects read-only, checks every region for 26 kinds of waste — idle and oversized resources, orphaned storage, networking waste, missing lifecycle and retention policies, commitment opportunities — and puts a dollar figure on each finding so you can work in priority order rather than guessing. Then it keeps scanning on a schedule, so the volume somebody orphans next month shows up as a finding instead of a slightly larger bill next quarter.
For what Parsivex looks for, see the finding types reference; for what actually happens during a scan and what data it reads, see how scans work.