EC2 and RDS get all the attention. But EBS — that boring storage layer underneath everything — might be silently costing you thousands.
Here's how to find and fix EBS waste.
EBS costs come in three flavors:
1. Volume storage: Pay per GB provisioned per month 2. IOPS: Pay for provisioned IOPS (on io1/io2) 3. Snapshots: Pay per GB stored
Sounds simple. It's not.
Because you pay for provisioned storage, not used storage. A 1TB volume that's 10% full costs the same as a 1TB volume that's 99% full.
And those snapshots? They stick around forever unless you delete them.
This is the most common waste.
"I might need more space later" → Provision 500GB Actual usage → 47GB
You're paying 10x what you need.
How to find them:aws cloudwatch get-metric-statistics \
--namespace AWS/EBS \
--metric-name VolumeReadOps \
--dimensions Name=VolumeId,Value=vol-xxx \
--start-time 2024-01-01T00:00:00Z \
--end-time 2024-01-31T23:59:59Z \
--period 86400 \
--statistics Sum
Low I/O + large size = probably oversized.
The fix: Resize volumes down. Yes, you can shrink EBS now (sort of). Create a smaller volume, copy data, swap.Instances get terminated. Volumes stick around.
"We might need that data" → Volume sits orphaned Months later → Nobody remembers what it was
How to find them:aws ec2 describe-volumes \
--filters Name=status,Values=available \
--query 'Volumes[*].{ID:VolumeId,Size:Size,Created:CreateTime}'
"Available" means attached to nothing.
The fix: Review each one. If nobody knows what it is, snapshot it (just in case) and delete the volume.Volume types have very different costs:
| Type | Cost/GB/month | Use Case | |------|---------------|----------| | gp3 | $0.08 | General purpose (most workloads) | | gp2 | $0.10 | Legacy general purpose | | io2 | $0.125+ | High IOPS requirements | | st1 | $0.045 | Throughput-optimized (big data) | | sc1 | $0.015 | Cold storage |
Running logs on gp2 when sc1 would work? That's 7x the cost.
How to find them:aws ec2 describe-volumes \
--query 'Volumes[*].{ID:VolumeId,Type:VolumeType,Size:Size,IOPS:Iops}'
Look for io1/io2 volumes with low IOPS utilization. Look for gp2 volumes (should mostly be gp3 by now).
The fix:Snapshots are cheap. But they add up.
Every daily backup creates a snapshot. Over years, you accumulate thousands.
I've seen companies with 50TB of snapshots for instances that no longer exist.
How to find them:aws ec2 describe-snapshots --owner-ids self \
--query 'Snapshots[*].{ID:SnapshotId,Size:VolumeSize,Start:StartTime,VolumeId:VolumeId}'
Look for:
io1/io2 volumes charge for provisioned IOPS. If you provision 10,000 IOPS but use 500, you're wasting money.
How to find them: Check CloudWatch forVolumeReadOps and VolumeWriteOps.
Provisioned IOPS >> Actual IOPS = waste.
The fix:If you're still on gp2, this is a quick win.
gp3 is:
Migration is online — no downtime required.
aws ec2 modify-volume \
--volume-id vol-xxx \
--volume-type gp3
For most volumes, this is free money.
Run the unattached volume query. Review anything new. Delete or justify.
Check snapshot growth. Verify lifecycle policies are working. Delete orphaned snapshots.
Are you using the right types? gp3 vs gp2? io2 vs gp3? st1 for cold data?
When instances die, check their volumes. Don't let them become orphans.
Set up DLM policies to:
This is free and built into AWS.
Alert on:
Tag volumes with owner and purpose. When an owner leaves, review their volumes.
EBS waste is boring. It's not as dramatic as a forgotten RDS instance.
But boring waste adds up. Thousands per month, quietly, without anyone noticing.
Spend an hour auditing your EBS. You'll find money.