To reduce an AWS bill, work the levers in order of effort-to-savings, not in the order the console suggests them. The first four — deleting orphaned resources, shutting non-production down outside business hours, migrating gp2 volumes to gp3, and fixing log retention — take a day or two combined and routinely cut 10–25% with zero architectural risk. Commitments (Savings Plans, Reserved Instances) come after rightsizing, never before, because committing to oversized capacity locks the waste in for one to three years.
What you are deciding
Every cost lever trades effort and risk against savings. The mistake most teams make is starting with the lever that sounds most sophisticated — a Graviton migration, a re-architecture — while unattached EBS volumes from 2023 quietly bill every hour. The honest ranking below puts each lever where it belongs. Savings percentages depend on your workload mix; the ordering rarely does.
What drives an AWS bill, mechanically: compute hours at on-demand rates, storage GB-months, data processing charges (NAT Gateway, load balancers, inter-AZ traffic), and per-request or per-GB service charges (S3 API calls, CloudWatch log ingestion). Each lever attacks one of those four.
What to check first
- Do you know your top five services by spend? If not, nothing else on this page is actionable yet — open Billing and Cost Management > Cost Explorer, group by Service, last three months.
- Is anything orphaned? Unattached EBS volumes, unassociated Elastic IPs, idle load balancers, snapshots of deleted volumes. These are pure waste.
- Does non-production run at night and on weekends? Dev and staging running 168 hours a week instead of 50 is a 70% overspend on that footprint.
- What is your commitment coverage? Steady-state compute running fully on-demand means you are paying the list rate for baseline load.
How to verify
Pull the service ranking and the obvious orphans before touching anything:
# Top services for the 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=SERVICE
# Unattached EBS volumes — pure waste, billing every hour
aws ec2 describe-volumes \
--filters Name=status,Values=available \
--query 'Volumes[].[VolumeId,Size,VolumeType,CreateTime]' \
--output table
# Elastic IPs allocated but not associated
aws ec2 describe-addresses \
--query 'Addresses[?AssociationId==null].[PublicIp,AllocationId]' \
--output table
In the console, Cost Explorer > Group by: Usage Type on your top service tells you the mechanism — instance hours versus data processing versus storage — which tells you which lever applies. Repeat per region if you run multi-region; orphans hide in regions nobody looks at.
The approach
Ranked by effort-to-savings, best first:
- Delete orphaned resources. Unattached volumes, old snapshots, unassociated Elastic IPs, load balancers with zero healthy targets, stopped instances still holding expensive EBS. Hours of work, immediate savings, no risk beyond confirming nobody needs the data (snapshot before deleting volumes).
- Schedule non-production off-hours. An EventBridge Scheduler rule or Instance Scheduler stopping dev/staging nights and weekends cuts those instance-hours by roughly two-thirds. RDS instances can be stopped too (they restart after seven days — schedule the re-stop).
- Migrate gp2 to gp3. gp3 has a lower per-GB rate and decouples IOPS from size. It is an online modification per volume, no downtime for most workloads. Check volumes over 1 TB carefully — provisioned throughput needs may change the math slightly.
- Fix CloudWatch Logs retention and ingestion. Log groups default to never-expire. Set retention (30–90 days for most application logs), and hunt debug-level logging shipping gigabytes a day.
- Rightsize compute. Use Compute Optimizer findings and CloudWatch data, not intuition — the gating rules are in Compute Optimizer right-sizing. Downsizing one instance size class is roughly a 50% cut on that instance.
- Buy commitments for the steady-state floor. After rightsizing, cover the baseline with Savings Plans or RIs. The decision matrix per workload shape is in RIs, Savings Plans, Spot, and on-demand.
- Add S3 lifecycle policies. Transition to Infrequent Access or Glacier tiers, expire incomplete multipart uploads, expire old versions on versioned buckets. Intelligent-Tiering is the low-effort default for unpredictable access patterns.
- Cut NAT Gateway data processing. S3 and DynamoDB gateway endpoints are free and eliminate NAT processing for that traffic. High-volume traffic to other AWS services may justify interface endpoints — do the math per service.
- Move to Graviton. Same performance tier at a lower rate for compatible workloads. Managed services first (RDS, ElastiCache, OpenSearch — an engine flag, not a code change), then containerized apps.
- Use Spot for interruption-tolerant work. CI runners, batch jobs, stateless workers in mixed ASGs. Deep discounts, real interruption risk — engineering effort required to handle it.
- Reduce cross-AZ and cross-region chatter. Inter-AZ data transfer is billed both directions. Keep chatty tiers AZ-local where the availability design allows; this is real re-architecture work.
- Re-platform the expensive tier. Aurora I/O-Optimized for I/O-heavy clusters, ElastiCache node rightsizing, moving fat queues off polling. Highest effort, workload-specific payoff.
When this is the wrong move
If the bill is spiking suddenly rather than drifting upward, stop — you have an incident, not an optimization problem, and a 60-minute triage comes first. If you are pre-product-market-fit and the bill is small, engineering time is worth more than the savings; do levers 1–4 and move on. And do not buy commitments during a migration or a major re-architecture — you will commit to the shape you are leaving.
Common mistakes
- Buying Savings Plans before rightsizing. The commitment locks in the oversized baseline for its full term.
- Optimizing the interesting service instead of the expensive one. Lambda tuning while EC2 is 60% of the bill.
- One-time cleanup with no follow-up. Orphans regrow. Without a monthly review the bill returns to trend within two quarters.
- Deleting without a snapshot. Unattached does not always mean unneeded; snapshot first, delete after a waiting period.
Keeping it under control
- Run a monthly one-hour cost review: top movers, new orphans, coverage percentage.
- Turn on Cost Anomaly Detection so spikes page someone instead of surfacing at invoice time.
- Enforce a tagging standard so spend maps to teams — untagged spend is unownable spend.
- Set AWS Budgets alerts at 80% actual and 100% forecast of the monthly expectation.
- Re-check commitment coverage quarterly; workloads drift and coverage should follow. The pricing mechanics live in the AWS Cost Management docs and calculator.aws for modelling changes.
The levers are not secrets; the discipline of working them in order, measuring the result, and repeating quarterly is what actually bends the curve. If nobody on your team truly owns the AWS environment end to end, that is the root of most of these problems — Stack Harbor runs AWS accounts daily under AWS Operations, and you can book an AWS review.