How to find unattached EBS volumes (and what they're costing you)
Last reviewed
An unattached EBS volume is a disk with no server. Nothing reads from it, nothing
writes to it — and AWS bills for every gigabyte of it, every month, for as long
as it exists.
In the console they show up with the state available, which sounds harmless.
It means "not attached to anything."
Where they come from
Nobody creates an unattached volume on purpose. They're left behind by:
- Terminating an instance whose data volumes had
DeleteOnTerminationset to false. The root disk goes; the data disk stays. (This is the default for volumes attached after launch.) - Migrations — copying data to a new volume and never deleting the old one.
- Infrastructure-as-code changes that detach a volume rather than destroy it.
- Debugging and load tests — a volume created to inspect a disk, then forgotten.
The longer an account has existed, the more of these it has.
What they cost
You pay for the provisioned size, not what's on the disk. A 500 GB volume with
2 GB of data costs the same as a full one.
In us-east-1:
| Volume type | Per GB-month | A forgotten 500 GB volume, per year |
|---|---|---|
| gp3 | $0.08 | $480 |
| gp2 | $0.10 | $600 |
Old forgotten volumes are usually gp2, the more expensive type, because they
predate gp3. Most other regions cost more again. And io1/io2 volumes also bill
for their provisioned IOPS on top of storage — an unattached one can cost several
times the figures above.
How to find every unattached volume yourself
Volumes are per region, and the console only shows one at a time. This checks every
region enabled on your account:
for r in $(aws ec2 describe-regions --query 'Regions[].RegionName' --output text); do
aws ec2 describe-volumes --region "$r" \
--filters Name=status,Values=available \
--query 'Volumes[].[VolumeId, VolumeType, Size, CreateTime, Tags[?Key==`Name`]|[0].Value]' \
--output text | sed "s/^/$r\t/"
doneEach line gives you the region, volume ID, type, size in GB, when it was created and
its Name tag. Size × your region's rate is what it costs you each month.
CreateTime is the useful column: a volume created two years ago and unattached
today is almost certainly forgotten.
How to delete them safely
Deleting a volume is permanent. Before you do:
1. Work out what it was. Check the Name tag and creation date. If it was created
from a snapshot, aws ec2 describe-volumes --volume-ids vol-… --query 'Volumes[].SnapshotId'
tells you which one.
2. If you're not sure, snapshot it first.
aws ec2 create-snapshot --volume-id vol-0123456789abcdef0 \
--description "archive before deleting unattached volume"Snapshots only bill for blocks actually written, at $0.05 per GB-month in us-east-1
— so a mostly-empty 500 GB volume becomes a snapshot costing well under a dollar a month.
3. Delete the volume.
aws ec2 delete-volume --volume-id vol-0123456789abcdef0Stop new ones appearing: for data volumes you won't need after an instance is
terminated, set DeleteOnTermination to true when you attach them. And if you're
cleaning up stopped instances, read
why a stopped EC2 instance still costs money
first — terminating them is exactly how most of these volumes get created.
The hard part is noticing
The loop above takes a minute. What doesn't happen is anyone running it after every
migration, deploy and load test, across every region.
Devopsly checks on every sync. Connect your AWS account read-only and it flags
every unattached volume in every region you have enabled, with an estimate of its
monthly storage cost based on the volume's own type and region. Delete one, and the
finding closes on the next sync.
It uses 15 read-only IAM actions and never touches your infrastructure or opens your
files.