Cloud infrastructure costs can increase even when an application does not receive more traffic. A development environment may remain active after a project is completed, an old database may continue running, or unused storage volumes may still generate monthly charges.
These resources are easy to overlook because they are often created during development, testing, temporary deployments, or troubleshooting.
The problem becomes more noticeable when an organization manages multiple AWS accounts, environments, and regions. A resource that was useful several months ago may no longer serve any purpose, but its cost continues to appear on the monthly bill.
Reducing cloud costs does not always require changing the application architecture or moving to cheaper services. A good starting point is identifying resources that are running, storing data, or generating charges without providing business value.
In this article, we will look at practical ways to find unused AWS resources, review their costs, and clean them up safely.
Why Unused AWS Resources Increase Cloud Costs
AWS provides services that can be created and removed through the console, command line, or infrastructure automation.
However, deleting an application or stopping an EC2 instance does not necessarily remove every resource associated with it.
For example, consider a development environment containing:
- An EC2 instance used for testing.
- An EBS volume attached to the instance.
- An Elastic IP address.
- A snapshot created before an upgrade.
- An old load balancer.
- A database created for a temporary feature.
Even if the application is no longer active, some of these resources may continue generating charges.
The most common sources of unnecessary costs include:
| Resource | Why it may generate unnecessary costs |
|---|
| EC2 instances | Running instances may remain unused |
| EBS volumes | Unattached volumes continue to incur storage charges |
| EBS snapshots | Old snapshots consume backup storage |
| Elastic IP addresses | Unassociated or otherwise chargeable public IPv4 addresses may incur costs |
| Load balancers | Old load balancers may remain active without traffic |
| RDS databases | Unused database instances continue running |
| NAT gateways | Unused gateways can generate hourly and data-processing charges |
| S3 storage | Old files, backups, and versions consume storage |
| CloudWatch Logs | Old log groups may retain large amounts of data |
Not every resource that appears inactive is unnecessary. Some resources are intentionally retained for backups, disaster recovery, compliance, or future deployments.
The goal is to identify resources that no longer have a valid purpose, not to delete everything that appears unused.
Start With AWS Cost Explorer
Before deleting resources, understand where the money is being spent.
AWS Cost Explorer helps analyze costs by service, account, region, usage type, and other dimensions.
For example, an organization may discover that its monthly EC2 spending has increased even though the application traffic has remained stable.
A useful review process is:
- Open AWS Cost Explorer.
- Select a suitable date range.
- Group costs by AWS service.
- Compare the current period with previous periods.
- Identify services with unexpected increases.
- Drill down into usage types where more detail is required.
Consider this example:
| AWS service | Monthly cost | Initial observation |
|---|
| EC2 | $420 | Review unused instances |
| RDS | $280 | Check development databases |
| NAT Gateway | $160 | Review network usage |
| EBS | $95 | Find unattached volumes |
| S3 | $75 | Review old files and versions |
These values are illustrative. Actual costs depend on region, configuration, usage, and pricing.
Cost Explorer identifies where spending occurs, but it does not automatically prove that a particular resource is safe to remove.
For that, you need to inspect the resources themselves.
Find Unused EC2 Instances
EC2 instances are among the first resources worth reviewing.
An instance may have been created for a temporary test, migration, or development task and then forgotten.
Check the following information:
- Instance state.
- CPU utilization.
- Network activity.
- Disk activity.
- Instance tags.
- Launch time.
- Attached storage.
- Application ownership.
A running instance with consistently low CPU utilization may deserve investigation, but low CPU usage alone does not mean it is unused. Applications can spend significant time waiting for network or database operations.
Review EC2 instances using AWS CLI
You can list EC2 instances in a region using:
aws ec2 describe-instances \
--region ap-south-1 \
--query "Reservations[].Instances[].[InstanceId,State.Name,InstanceType,LaunchTime]" \
--output table
This command displays the instance ID, state, instance type, and launch time.
To identify instances with missing owner information, review their tags:
aws ec2 describe-instances \
--region ap-south-1 \
--query "Reservations[].Instances[].[InstanceId,Tags]" \
--output table
Tags such as Environment, Owner, Project, and CostCenter make resource ownership easier to establish.
Check actual resource utilization
Use Amazon CloudWatch metrics to review CPU utilization, network traffic, and other available instance metrics.
Review a meaningful period rather than relying on one day of data.
For example, an instance may appear inactive during a weekend but still support a monthly reporting process.
Before stopping or terminating an instance, confirm its purpose with the owner and check whether any production workload depends on it.
Identify Unattached EBS Volumes
Stopping or terminating an EC2 instance does not automatically remove every EBS volume.
An unattached volume can continue generating storage charges.
List EBS volumes using:
aws ec2 describe-volumes \
--region ap-south-1 \
--query "Volumes[].[VolumeId,State,Size,VolumeType,CreateTime]" \
--output table
Look for volumes with a state of available.
These volumes are not currently attached to an EC2 instance.
However, do not immediately delete every available volume. Some may contain important backups or data waiting for another instance.
Before deleting a volume:
- Identify the original instance or application.
- Check its tags and creation date.
- Confirm whether the data is still required.
- Create a snapshot if the data must be preserved.
- Obtain approval from the resource owner.
- Delete the volume only after verification.
This process prevents accidental data loss while removing unnecessary storage costs.
Review Old EBS Snapshots
EBS snapshots are useful for backup and recovery, but old snapshots can accumulate over time.
Review snapshot ownership, creation dates, and associated volumes.
You can list snapshots owned by the current account:
aws ec2 describe-snapshots \
--owner-ids self \
--region ap-south-1 \
--query "Snapshots[].[SnapshotId,StartTime,VolumeSize,Description]" \
--output table
Do not delete snapshots simply because their original EC2 instance no longer exists.
A snapshot may be part of a recovery plan or a required backup retention period.
A better approach is to define a retention policy based on business requirements. For example, development snapshots may have a shorter retention period than production backups.
AWS Data Lifecycle Manager can help automate snapshot creation and retention for supported EBS resources.
Find Unused Elastic IP Addresses
Public IPv4 addresses may generate charges depending on their allocation and usage.
An Elastic IP address that is no longer associated with an application should be reviewed.
Use:
aws ec2 describe-addresses \
--region ap-south-1 \
--query "Addresses[].[PublicIp,AllocationId,AssociationId,InstanceId]" \
--output table
Review addresses that do not have an active association.
Before releasing an address, confirm that it is not reserved for a future deployment, DNS configuration, firewall rule, or external integration.
If an application uses a fixed public IP address, changing it without planning can interrupt connectivity.
Review NAT Gateways and Load Balancers
Network resources can generate costs even when application traffic is low.
NAT Gateways
A NAT Gateway can generate hourly and data-processing charges.
Check whether a development environment still needs its NAT Gateway. Also review CloudWatch metrics such as bytes processed and connection activity.
An unused NAT Gateway may be a good candidate for removal, but first check private subnet route tables.
Deleting a NAT Gateway while workloads still depend on it can prevent private instances from accessing external services.
Load balancers
Old Application Load Balancers and Network Load Balancers may remain active after an application has been removed.
Use the AWS console or CLI to inspect load balancers, listeners, target groups, and registered targets.
A load balancer with no healthy targets may indicate an abandoned deployment, but it may also be temporarily inactive.
Confirm its ownership and routing dependencies before removing it.
Find Unused RDS Database Instances
Database instances can become a significant part of the monthly cloud bill.
Development databases, testing databases, and temporary migration environments are common sources of unnecessary spending.
List RDS instances using:
aws rds describe-db-instances \
--region ap-south-1 \
--query "DBInstances[].[DBInstanceIdentifier,DBInstanceStatus,DBInstanceClass,Engine,AllocatedStorage]" \
--output table
Review:
- Database status.
- Instance class.
- CPU utilization.
- Database connections.
- Storage usage.
- Backup retention.
- Application ownership.
- Recent connection activity.
An instance with no recent connections may be unused, but scheduled jobs and infrequent reporting tasks can make activity difficult to interpret.
For development environments, consider whether stopping and starting an instance according to a schedule is appropriate.
Remember that stopping an RDS instance does not necessarily eliminate every cost. Storage and retained backups can continue generating charges, and availability or restart behavior depends on the database service and configuration.
For production databases, prioritize rightsizing and workload analysis rather than deleting resources based on low activity.
Clean Up Unused S3 Storage
Amazon S3 can accumulate old files, temporary exports, incomplete multipart uploads, and previous object versions.
Review the following:
- Old application exports.
- Temporary upload files.
- Abandoned backup files.
- Previous object versions.
- Incomplete multipart uploads.
- Unused development data.
S3 Lifecycle rules can automatically transition or expire objects based on age, storage class, or other supported conditions.
For example, a development bucket might use a lifecycle policy that removes temporary exports after a defined retention period.
Before configuring expiration, check whether versioning, object lock, legal retention, or recovery requirements apply.
Never apply a broad deletion rule to a production bucket without testing its effect on real data.
Use AWS Cost Optimization Tools
Manually checking every service and region becomes difficult as an AWS environment grows.
Several AWS tools can help identify opportunities.
| Tool | Purpose |
|---|
| AWS Cost Explorer | Analyze spending and usage trends |
| AWS Budgets | Set spending thresholds and alerts |
| AWS Compute Optimizer | Identify rightsizing recommendations for supported resources |
| AWS Trusted Advisor | Review selected cost, security, and operational checks based on plan availability |
| AWS Config | Track resource configuration and changes |
| CloudWatch | Monitor resource utilization and activity |
These tools provide different types of information.
For example, Compute Optimizer can recommend a smaller EC2 instance based on observed utilization. However, the recommendation should be reviewed against application performance, peak traffic, and scaling requirements before implementation.
Automate Resource Cleanup Safely
Once unused resources have been identified, cleanup can be automated.
However, automatic deletion based only on resource age or low utilization is risky.
A safer strategy is to use resource tags and approval-based cleanup.
For example, require every development resource to have these tags:
Environment = Development
Owner = DevelopmentTeam
Project = CustomerPortal
ExpiryDate = 2026-11-30
A scheduled cleanup process can identify resources whose expiry date has passed.
Instead of deleting them immediately, the process can:
- Find expired resources.
- Notify the resource owner.
- Generate a cleanup report.
- Wait for an approval period.
- Stop or remove approved resources.
- Record the action for auditing.
AWS Config rules, EventBridge, Lambda, and infrastructure automation tools can support this workflow.
For production resources, require stronger approval and dependency checks.
Common Mistakes to Avoid
Deleting resources based only on low utilization
A database or server may be quiet during normal business hours but essential during scheduled workloads.
Review a representative period and understand the workload before making changes.
Ignoring dependencies
A storage volume, IP address, or network gateway may support another application even if its original owner is no longer available.
Check dependencies before cleanup.
Removing backups to reduce storage costs
Old snapshots and object versions may be necessary for recovery, compliance, or investigation.
Use a defined retention policy instead of deleting backups without review.
Reviewing only one AWS region
Resources may exist in multiple regions and accounts.
A complete review should cover every region and AWS account within the organization's scope.
Making changes without recording them
Cleanup operations should be auditable. Record the resource ID, owner, reason for removal, approval, and completion time.
A Practical Monthly AWS Cost Review
A monthly review helps prevent unused resources from accumulating.
Use this checklist:
- Compare current AWS spending with previous months.
- Identify services with unexpected cost increases.
- Review EC2 instances with consistently low utilization.
- Find unattached EBS volumes and old snapshots.
- Check unused Elastic IP addresses.
- Review NAT Gateways and load balancers.
- Identify development and test databases that are no longer needed.
- Review old S3 objects and incomplete uploads.
- Check resource ownership and expiry tags.
- Record approved cleanup actions and verify the cost impact.
After cleanup, compare the next billing period with the previous one. Remember that cost changes may not appear immediately, and savings depend on the resource type, billing model, and remaining dependencies.
Summary
Reducing AWS costs starts with understanding which resources are consuming money and whether those resources still provide business value.
AWS Cost Explorer helps identify expensive services, while CloudWatch, Compute Optimizer, AWS Config, and service-specific tools help investigate resource usage.
Start by reviewing EC2 instances, unattached EBS volumes, old snapshots, Elastic IP addresses, NAT Gateways, RDS databases, and S3 storage. Use ownership tags, retention policies, and approval-based cleanup to avoid removing resources that are still required.
A regular cost review, combined with automated reporting and controlled cleanup, helps keep AWS infrastructure efficient without creating unnecessary production risks.
Join the conversation! Your thoughts help the community grow.