Every AWS account that has been alive for more than a year carries orphans: EBS volumes detached from long-terminated instances, snapshots of servers nobody remembers, load balancers pointing at empty target groups, Elastic IPs allocated for a migration that finished in 2023. None of them do anything except appear on the invoice. A structured hunt — inventory, verify idle, snapshot what is deletable-but-scary, then delete — typically trims a visible slice off the monthly bill in an afternoon, with near-zero risk if you follow the order below.
What you are deciding
For each candidate resource you are making one of three calls: delete now (provably orphaned, no data value), archive then delete (data might matter — snapshot the volume, archive the snapshot, then remove the original), or keep with an owner and a tag (actually in use, just anonymous). The mistake to avoid is treating this as a pure deletion exercise: the lasting win is that everything that survives the hunt ends up tagged with an owner, so the next hunt takes minutes instead of hours.
The usual suspects, in rough order of wasted spend: unattached EBS volumes (billed per GB-month whether attached or not), aged snapshots that accumulate forever, idle load balancers (billed hourly even with zero traffic), unassociated Elastic IPs — and note that since AWS began charging for all public IPv4 addresses, every idle EIP is pure waste — plus stopped instances whose storage still bills, orphaned NAT Gateways in abandoned VPCs, and dev RDS instances nobody has connected to in months.
What to check first
- Trusted Advisor’s cost checks under Trusted Advisor > Recommendations > Cost optimization — the full set requires a Business or Enterprise Support plan; on Basic/Developer you only get a subset.
- Cost Explorer grouped by usage type for the last three months —
EBS:VolumeUsage,ElasticIP:IdleAddress, and LoadBalancerUsage lines tell you which category is worth hunting first. - Regions you forgot. Orphans love the region someone tried once in 2022. Check every region with any spend in Cost Explorer.
- Tag coverage. If most resources lack an owner tag, plan the hunt as inventory-plus-tagging, not just deletion.
How to verify
Run the inventory per region — these are read-only:
# Unattached EBS volumes
aws ec2 describe-volumes --filters Name=status,Values=available \
--query 'Volumes[].[VolumeId,Size,VolumeType,CreateTime]' --output table
# Elastic IPs associated with nothing
aws ec2 describe-addresses \
--query 'Addresses[?AssociationId==null].[PublicIp,AllocationId]' --output table
# Snapshots you own, oldest first
aws ec2 describe-snapshots --owner-ids self \
--query 'sort_by(Snapshots,&StartTime)[].[SnapshotId,VolumeId,StartTime,VolumeSize]' \
--output table
# Load balancers, then check each one's targets
aws elbv2 describe-load-balancers \
--query 'LoadBalancers[].[LoadBalancerName,CreatedTime]' --output table
aws elbv2 describe-target-health --target-group-arn <target-group-arn>
A load balancer with registered healthy targets can still be idle — confirm with traffic over the last 30 days before touching it:
aws cloudwatch get-metric-statistics --namespace AWS/ApplicationELB \
--metric-name RequestCount \
--dimensions Name=LoadBalancer,Value=app/old-alb/0123456789abcdef \
--start-time 2026-06-04T00:00:00Z --end-time 2026-07-04T00:00:00Z \
--period 86400 --statistics Sum
Zero or near-zero daily sums for a month is a strong idle signal. The console equivalents are EC2 > Elastic Block Store > Volumes (filter State: Available), EC2 > Network & Security > Elastic IPs, and each load balancer’s Monitoring tab. The EC2 documentation covers the deletion semantics for each resource type if you want the fine print.
The runbook
- Unassociated Elastic IPs first. Zero data, zero risk, immediate stop to the hourly charge. Release them under EC2 > Elastic IPs > Release Elastic IP addresses. Only pause if one is whitelisted at a partner — check before releasing, because you cannot get the same address back later.
- Unattached EBS volumes, with a safety snapshot. For each
availablevolume: snapshot it, tag the snapshot with the origin volume ID and a delete-after date, then delete the volume. The snapshot costs a fraction of the live volume and preserves your undo. Details on the storage math are in the EBS and snapshot lifecycle article. - Aged snapshots. Delete manual snapshots older than your real retention need, oldest first. Two traps: snapshots are incremental, so deleting one does not always free its full listed size; and snapshots backing registered AMIs cannot be deleted until the AMI is deregistered — deregister dead AMIs first.
- Idle load balancers. After the RequestCount check, delete the load balancer, then its now-orphaned target groups. If DNS anywhere still points at its name, fix that first — a dangling CNAME is how a cleanup becomes an incident.
- Stopped-but-storaged instances. A stopped instance costs nothing for compute but everything for its EBS. If it has been stopped for 90+ days, snapshot the volumes, create an AMI if you are sentimental, and terminate.
- Orphaned NAT Gateways and interface endpoints. Both bill hourly in VPCs nobody uses anymore. Confirm the VPC has no running instances before deleting.
- Tag everything that survives. Owner, environment, and a review date. This converts a one-off purge into a repeatable process.
When this is the wrong move
Do not run a mass deletion during a migration or a DR test window — “unattached” volumes and “idle” load balancers are sometimes mid-flight infrastructure. Skip anything belonging to a compliance-scoped system until you have confirmed retention requirements; some snapshots exist because an auditor said so. And if the account has no tagging discipline at all, do the inventory-and-tag pass first and the deletions a week later, after owners have had a chance to object.
Common mistakes
- Releasing an Elastic IP that a customer or partner firewall had whitelisted — the address goes back to the pool and is effectively unrecoverable.
- Deleting snapshots that back a registered AMI, then wondering why the deregistration cleanup later finds nothing to delete.
- Judging a load balancer idle from a one-day metric window instead of a full month that includes billing cycles and batch jobs.
- Cleaning only the home region and declaring victory while three forgotten regions keep billing.
Prevention
- A monthly 30-minute orphan review beats a yearly archaeology dig.
- Enforce owner/environment tags at creation time with tag policies or IAM conditions.
- Add lifecycle automation for snapshots via Data Lifecycle Manager so aged-snapshot hunts become unnecessary.
- Alert on
availablevolume count and unassociated EIP count with a scheduled script or AWS Config rules. - Make teardown part of every project’s definition of done — orphans are almost always the ghost of a finished project.
The orphan hunt is the easiest money in AWS cost work because nothing you remove was doing anything. 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.