How to Find Unattached EBS Volumes in AWS (and Safely Delete Them)

Unattached EBS volumes are the purest form of cloud waste. Nothing is reading them, nothing is writing to them, no application would notice if they vanished, and they bill every hour anyway.

They accumulate quietly. An engineer terminates an instance and the data volume survives because DeleteOnTermination was false. Someone detaches a volume to migrate data and never comes back to it. An Auto Scaling launch half-fails. None of these leave an alert behind, and none of them show up as a line item you would recognise on a bill, because EBS charges land in one aggregate storage figure.

Why they cost more than people expect

EBS bills on provisioned capacity, not on use. You pay for the size you asked for, whether the volume holds a production database or nothing at all.

That means a 500 GB gp3 volume costs the same detached as it did attached. At roughly $0.08 per GB-month for gp3 in us-east-1, that single forgotten volume is about $40 a month, or close to $500 a year, for storage nobody has read since the instance was terminated. Older gp2 volumes cost about 25 percent more per GB for the same capacity.

Two extras catch people out:

  • Provisioned IOPS keep billing. On io1 and io2 volumes, IOPS are a separate charge from capacity. A detached io2 volume with high provisioned IOPS can cost several times what its gigabytes suggest.
  • Snapshots outlive the volume. Teams often snapshot before deleting, then forget the snapshot. Snapshot storage is cheaper per GB, but an unmanaged pile of them becomes its own line item.

Finding them in the console

The EC2 console shows this in one place. Open EC2 → Elastic Block Store → Volumes and filter on State = available. Anything listed is attached to nothing.

Add the Created column and sort by it. Volumes created months ago are the safest deletion candidates, and they are usually the ones nobody remembers.

The catch is that this view is per-region. Waste hides in the regions you do not think about: the one where someone tested a service in 2024, the one a Terraform default put resources in. Check every region you have ever used, not the two you look at daily.

Finding them from the CLI

The CLI is the practical option once you have more than one region or account. This lists available volumes in a single region with the fields you need to make a decision:

aws ec2 describe-volumes \
  --filters Name=status,Values=available \
  --query 'Volumes[].{ID:VolumeId,GiB:Size,Type:VolumeType,AZ:AvailabilityZone,Created:CreateTime}' \
  --output table

To sweep every region, loop over the region list:

for region in $(aws ec2 describe-regions --query 'Regions[].RegionName' --output text); do
  echo "== $region"
  aws ec2 describe-volumes --region "$region" \
    --filters Name=status,Values=available \
    --query 'Volumes[].[VolumeId,Size,VolumeType,CreateTime]' \
    --output text
done

Run that once per account. If you have an AWS Organization, repeat it under an assumed role in each member account, which is where the total usually turns out to be larger than anyone guessed.

Deciding what is actually safe to delete

“Available” means nothing is attached right now. It does not mean the data is worthless. A volume detached yesterday during a migration looks identical to one abandoned two years ago.

Work through the candidates in this order:

  1. Sort by creation date. Anything older than a few months with no tags is almost always genuine waste.
  2. Read the tags. An owner, a stack name, or a Name referencing a long-dead service tells you most of what you need.
  3. Snapshot anything you cannot fully account for. A snapshot costs a fraction of the volume and buys you a reversible decision.
  4. Give owners a window. A short message listing the volumes and a 48 hour deadline catches the “wait, I need that” cases before they become an incident.
  5. Delete in batches, oldest first, and leave a week between batches.

One related check while you are in here: volumes attached to stopped instances do not show as available, and they bill exactly the same. A stopped EC2 instance costs nothing for compute and keeps charging for every attached gigabyte. Those are worth a separate pass.

Stopping them coming back

Cleanup is a one-off. Prevention is what keeps the number near zero:

  • Set DeleteOnTermination on data volumes where the data genuinely is disposable. It defaults to true for root volumes and false for additional volumes, which is exactly why extra volumes are the ones that pile up.
  • Require an owner tag on volume creation, enforced through an SCP or your IaC review, so a future you knows who to ask.
  • Add an AWS Config rule. The managed ec2-volume-inuse-check rule flags volumes that are not in use, and can optionally flag ones without DeleteOnTermination set.
  • Put a retention rule on snapshots so the snapshots you take during cleanup do not become the next thing to clean up.

Trusted Advisor has a related check, but the underutilised-volume check sits behind Business or Enterprise support, and it reports low utilisation rather than the detached state specifically. Config plus a scheduled sweep is the cheaper path for most teams.

Doing the sweep continuously

Everything above works, and it works exactly once. Waste regrows the week after you finish, because the behaviours that create it are normal engineering behaviours, not mistakes anyone will stop making.

The honest version of this job is a scheduled read-only sweep across every region and every account, with a dollar figure attached so you can sort by what actually matters. That is what Nuvozy does: it connects with a read-only role, no agents and no write access, checks every region for unattached volumes, orphaned snapshots, idle instances, and dozens of other waste patterns, and prices each finding using live AWS pricing so the cleanup list is already sorted by impact.

The first scan is free, which is usually enough to find out whether this is a $40 problem or a $4,000 one.

Frequently asked questions

Do unattached EBS volumes cost money? +

Yes. EBS is billed on provisioned capacity, not on use. A volume in the 'available' state is attached to nothing and read by nobody, and it still bills for every provisioned GB every hour until you delete it. Provisioned IOPS on io1 and io2 volumes bill separately and also keep charging.

How do I find unattached EBS volumes? +

In the EC2 console, open Volumes and filter on State = available. From the CLI, run 'aws ec2 describe-volumes --filters Name=status,Values=available'. Both are per-region, so you have to repeat the check in every region you have ever launched anything in, not just the ones you use today.

Is it safe to delete an available EBS volume? +

Usually, but not always. 'Available' means nothing is attached right now, not that the data is worthless. Take a snapshot first, check the tags and creation time for an owner, and give that owner a short window to object. Snapshots cost a fraction of the volume, so this is cheap insurance.

Why do unattached volumes keep appearing? +

The usual cause is DeleteOnTermination being false on attached volumes, which is the default for additional data volumes. Terminating the instance leaves the volume behind. Detached volumes from manual migrations, failed Auto Scaling launches, and restored-then-abandoned snapshots make up most of the rest.

Related reading