---
title: "Find unused AWS resources: cleanup checklist | Silamir Group"
description: "Unattached EBS volumes, orphaned snapshots, public IPs, gp2 to gp3, EC2 rightsizing: the command that detects each resource and the saving it brings."
url: https://audit.aws.silamir.com/en/resources/unused-aws-resources
lang: en
updated: 2026-10-24
---

FinOps

# How to find unused AWS resources: a cleanup checklist

The most common unused AWS resources are unattached EBS volumes, snapshots whose volume is gone, public IPv4 addresses with no purpose, stopped instances that keep their disks, load balancers and NAT gateways with no traffic, and logs with no retention. For each type, this checklist gives the AWS CLI command that detects it and what it costs.

Published October 24, 2026 · 6 min read

## In short

- An unused resource is still billed: a detached volume per GB, a public IP per hour, a NAT gateway per hour even with no traffic.
- The clean-up does not touch production, but always follows this order: find the owner, back up if needed, wait, delete.
- Right-sizing and moving from gp2 to gp3 are about badly used rather than unused resources: they are in the same checklist because they are detected the same way.

## What each forgotten resource costs

AWS public on-demand prices in eu-west-3 (Paris), taken from the AWS Price List API on 23 October 2026, in US dollars, on the basis of 730 hours a month.

| Resource | What is billed | Example monthly saving |
| --- | --- | --- |
| **Unattached EBS volume** | USD 0.0928/GB-month in gp3, USD 0.116 in gp2 | USD 46.40 for a 500 GB gp3 volume |
| **Old or orphaned EBS snapshot** | USD 0.053/GB-month (USD 0.01325 in archive) | Up to USD 53 per TB deleted, USD 39.75 per TB archived |
| **Public IPv4 address** | USD 0.005/hour, attached or not | USD 3.65 per address |
| **Stopped EC2 instance** | Its EBS volumes and its Elastic IP | USD 12.93 for 100 GB of gp3 and one Elastic IP |
| **Application Load Balancer with no traffic** | USD 0.02646/hour, plus the LCUs used | USD 19.32 per load balancer |
| **NAT gateway with no traffic** | USD 0.05/hour, plus USD 0.05/GB processed | USD 36.50 per NAT gateway |
| **Interface VPC endpoint with no traffic** | USD 0.011/hour per Availability Zone | USD 24.09 for an endpoint across 3 zones |
| **CloudWatch logs with no retention** | USD 0.0315/GB-month stored | USD 31.50 per TB of logs deleted |
| **gp2 volume (instead of gp3)** | 20% more than gp3 | USD 23.20 per TB migrated |
| **Over-provisioned instance** | The larger size | USD 85.85 for an m7i.xlarge brought down to m7i.large |

## Storage: volumes, snapshots, AMIs

- **Unattached EBS volumes** (status `available`): often left behind when an instance was terminated without the delete-on-termination option.
- **Orphaned snapshots**: their source volume no longer exists. As snapshots are incremental, the space freed may be smaller than the size shown, but an old full snapshot frees almost all of it.
- **Snapshots older than six months**: delete them if no backup policy relies on them, or archive them (75% cheaper, 90 days minimum, restore fees).
- **AMIs older than a year**: deregistering an AMI does not delete its snapshots by default; they are billed as long as they exist.
- **gp2 volumes**: live migration to gp3, no stop needed. Above 1 TB, provision on gp3 the IOPS the volume had on gp2 (3 IOPS per GB).

```
# Unattached volumes
aws ec2 describe-volumes --filters Name=status,Values=available \
  --query 'Volumes[].[VolumeId,Size,VolumeType,CreateTime]' --output table

# Volumes referenced by a snapshot but no longer present (orphaned snapshots)
comm -23 \
  <(aws ec2 describe-snapshots --owner-ids self --query 'Snapshots[].VolumeId' --output text | tr '\t' '\n' | sort -u) \
  <(aws ec2 describe-volumes --query 'Volumes[].VolumeId' --output text | tr '\t' '\n' | sort -u)

# Snapshots older than a date
aws ec2 describe-snapshots --owner-ids self \
  --query "Snapshots[?StartTime<='2026-04-24'].[SnapshotId,VolumeSize,StartTime,Description]" --output table

# Archive a snapshot instead of deleting it
aws ec2 modify-snapshot-tier --snapshot-id snap-0123456789abcdef0 --storage-tier archive

# AMIs older than a year
aws ec2 describe-images --owners self \
  --query "Images[?CreationDate<='2025-10-24'].[ImageId,Name,CreationDate]" --output table
```

## Network: IP addresses, load balancers, NAT, endpoints

- **Unassociated Elastic IP addresses**, and more broadly any public IPv4 address that does not need to be public: since February 2024, AWS charges for all of them, in use or not.
- **Load balancers with no traffic**: no request over 30 days, or no registered target.
- **NAT gateways with no traffic**: a deleted environment often leaves its NAT behind, billed by the hour. NAT costs are detailed in [AWS NAT gateway cost](https://audit.aws.silamir.com/en/resources/aws-nat-gateway-cost).
- **Interface VPC endpoints with no traffic**, or deployed in more zones than the workloads using them.

```
# Unassociated Elastic IPs
aws ec2 describe-addresses \
  --query 'Addresses[?AssociationId==`null`].[PublicIp,AllocationId]' --output table

# Every network interface carrying a public IPv4 address
aws ec2 describe-network-interfaces \
  --query 'NetworkInterfaces[?Association.PublicIp].[NetworkInterfaceId,Association.PublicIp,InterfaceType]' --output table

# Requests received by an ALB over 30 days
aws cloudwatch get-metric-statistics --namespace AWS/ApplicationELB --metric-name RequestCount \
  --dimensions Name=LoadBalancer,Value=app/my-alb/0123456789abcdef \
  --start-time 2026-09-24T00:00:00Z --end-time 2026-10-24T00:00:00Z \
  --period 2592000 --statistics Sum

# Bytes sent out by a NAT gateway over 30 days
aws cloudwatch get-metric-statistics --namespace AWS/NATGateway --metric-name BytesOutToDestination \
  --dimensions Name=NatGatewayId,Value=nat-0123456789abcdef0 \
  --start-time 2026-09-24T00:00:00Z --end-time 2026-10-24T00:00:00Z \
  --period 2592000 --statistics Sum
```

## Compute: stopped, over-provisioned and always-on instances

- **Long-stopped instances**: the instance is no longer billed, its volumes and Elastic IP still are. An image (AMI) then termination frees everything.
- **Over-provisioned instances**: Compute Optimizer flags them `Overprovisioned` from the last 14 days of metrics. Without the CloudWatch agent, memory is not taken into account.
- **Non-production running around the clock**: at 12 hours per working day, an instance goes from 730 to about 260 hours a month. A stopped RDS instance restarts on its own after seven days.

```
# Stopped instances, with the stop date
aws ec2 describe-instances --filters Name=instance-state-name,Values=stopped \
  --query 'Reservations[].Instances[].[InstanceId,InstanceType,StateTransitionReason]' --output table

# Over-provisioned instances according to Compute Optimizer
aws compute-optimizer get-ec2-instance-recommendations \
  --query 'instanceRecommendations[?finding==`OVER_PROVISIONED`].[instanceArn,currentInstanceType,recommendationOptions[0].instanceType]' \
  --output table
```

## Logs and S3: what grows quietly

- **CloudWatch log groups with no retention**: every gigabyte ingested stays stored forever.
- **Incomplete S3 multipart uploads**: parts already sent are stored and billed without showing up as objects. An `AbortIncompleteMultipartUpload` lifecycle rule purges them.
- **Rarely read S3 objects in the Standard class**: Standard-IA costs USD 0.0131/GB-month and Glacier Instant Retrieval USD 0.005, against USD 0.024 in Standard.

```
# Log groups with no retention
aws logs describe-log-groups \
  --query 'logGroups[?retentionInDays==`null`].[logGroupName,storedBytes]' --output table

# Multipart uploads in progress on a bucket
aws s3api list-multipart-uploads --bucket my-bucket \
  --query 'Uploads[].[Key,Initiated]' --output table
```

## Deleting without breaking anything: the method

- **Find the owner** through tags, CloudTrail (who created the resource) or the name. With no owner, tag the resource "to delete on DD/MM" and warn the teams.
- **Back up what cannot be replaced**: a snapshot of a volume, an AMI of a stopped instance. An archived snapshot costs seven times less than a gp3 volume.
- **Wait** one or two weeks: a resource someone needs is quickly claimed.
- **Delete**, then check the drop in Cost Explorer the following month.
- **Stop it coming back**: mandatory owner tag, per-team budget, default retention on logs. The other levers are in the guide to [reduce your AWS bill](https://audit.aws.silamir.com/en/resources/reduce-aws-bill).

## What the FinOps audit detects automatically

The [AWS FinOps audit](https://audit.aws.silamir.com/en/aws-finops-audit) runs these searches read-only, in every Region you choose: unattached EBS volumes, unassociated Elastic IPs, snapshots older than six months, AMIs older than a year, log groups with no retention, VPC endpoints with no traffic over 30 days. It adds Compute Optimizer's right-sizing recommendations and Trusted Advisor's cost optimisation checks.

Each resource is named by its identifier, with the monthly saving and the command to act. The resilience domain flags in parallel the snapshots and backups to keep, so the only copy of a database is not deleted. The audit deletes nothing: it recommends, you decide.

## Unused AWS resources: frequently asked questions

**How do I find unused resources on AWS?**

With the AWS CLI describe commands, Region by Region: volumes with status available, Elastic IPs with no association, snapshots whose volume no longer exists, log groups with no retention. Trusted Advisor and Compute Optimizer add under-used instances and volumes.

**Is a stopped EC2 instance billed?**

The instance itself is not, but its EBS volumes and its Elastic IP address are still billed. To free everything, create an image (AMI) if needed, then terminate the instance.

**Does deleting a snapshot free its full size?**

Not always. EBS snapshots are incremental: only blocks referenced by no other snapshot are removed. The size shown is therefore a maximum.

**Does moving from gp2 to gp3 require downtime?**

No. Changing the volume type happens live. gp3 costs 20% less and includes 3,000 IOPS and 125 MiB/s; for a gp2 volume above 1 TB, provision the extra IOPS to keep the same performance.

**Do I need to check every Region?**

Yes. Forgotten resources often sit in a Region used for a test and then abandoned. The commands on this page are regional: repeat them with the --region option for each active Region.

## Where does your AWS account stand?

Ten questions, three minutes, no access to your account: a security score, a FinOps score and the article to read for each of your three weakest points. Your answers stay in your browser.

[Take the free self-assessment](https://audit.aws.silamir.com/en/resources/aws-self-assessment)

## Read next

- **[Savings Plans or RIs](https://audit.aws.silamir.com/en/resources/savings-plans-vs-reserved-instances)**: Compute or EC2 Instance Savings Plan, Reserved Instances, Database Savings Plans: decision tree, how to size the commitment, pitfalls and a priced example.
- **[NAT gateway cost](https://audit.aws.silamir.com/en/resources/aws-nat-gateway-cost)**: AWS NAT Gateway pricing: USD 0.05 per hour and USD 0.05 per GB processed in eu-west-3. A priced example and six ways to cut the bill: endpoints, zones, IPv6, non-prod.
- **[Well-Architected review cost](https://audit.aws.silamir.com/en/resources/aws-well-architected-review-cost)**: The Well-Architected Tool is free: a review costs time, in-house or with a partner. Options compared, AWS funding and what an automated audit adds.

## Ready to audit your AWS account?

Subscribe on AWS Marketplace, deploy the read-only role and get your reports within the hour.

[Subscribe on AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-llvpm5sa4mljs) · [See how it works](https://audit.aws.silamir.com/en/how-it-works)
