For small engineering teams, managing AWS spending is often secondary to shipping features. However, resource leakage—such as orphaned database backups, oversized virtual servers, and unused IP addresses—can quietly inflate your bill.

This guide covers a practical checklist for reviewing AWS storage, compute sizing, environment schedules, and data usage. The effect on spend depends on each account's workloads, regions, and current configuration.

First checks: Review detached storage volumes and unused public IP allocations, then confirm whether they can be safely removed.

The Checklist at a Glance

  • Clean up Orphaned Resources: Detached EBS volumes, unused Elastic IPs (EIPs), and old snapshots.
  • Right-Size Compute Instances: Downsizing oversized EC2, ECS, or RDS instances.
  • Automate Dev Environment Schedules: Turning off staging and testing nodes outside working hours.
  • Optimize Storage: Applying S3 lifecycle rules to archive log files.
  • Upgrade to Modern Families: Moving workloads to Graviton (ARM) instances.

1. Cleaning up Orphaned Storage (EBS)

When you terminate an EC2 instance, its attached Elastic Block Store (EBS) volume is often preserved on disk. These "detached" volumes continue to accumulate storage fees.

Run this AWS CLI command to list all detached EBS volumes currently incurring charges in your default region:

terminal
dinesh@devops ~ ❯ aws ec2 describe-volumes --filters Name=status,Values=available --query "Volumes[*].{ID:VolumeId,Size:Size}" --output table
-----------------------
|   DescribeVolumes   |
+---------------------+
|  ID          | Size |
+--------------+------+
|  vol-08a...  |  100 |
|  vol-0cf...  |   50 |
+--------------+------+

After confirming a detached volume is no longer needed, delete it with aws ec2 delete-volume --volume-id <id>. Review its tags, snapshots, and recovery requirements before removal.

2. Stop Paying for Idle Public IPs

AWS charges for Elastic IP addresses that are allocated to your account but not attached to a running EC2 instance. Identify and release these unused resources:

terminal
dinesh@devops ~ ❯ aws ec2 describe-addresses --query "Addresses[?InstanceId==null].{IP:PublicIp,AllocationId:AllocationId}" --output table

3. Automate Nightly Shutdowns

Development and staging resources may not need to run outside active work periods. Scheduling can reduce compute runtime, but check dependencies, startup behavior, and attached-resource charges before automating shutdowns.

Implement a lightweight cron script or utilize **AWS Instance Scheduler** to turn off non-essential nodes at 7:00 PM and restart them at 8:00 AM on weekdays.

4. S3 Lifecycle Storage Policies

If you write application logs or database backups to Amazon S3, they accumulate over time, increasing your storage costs. Configure an S3 Lifecycle Rule to transition older objects to colder storage or delete them automatically.

This rule transitions objects in the /logs path to **S3 Glacier Flexible Retrieval** after 30 days, and deletes them after 90 days:

s3_lifecycle.json
{
  "Rules": [
    {
      "ID": "MoveLogsToGlacier",
      "Status": "Enabled",
      "Filter": { "Prefix": "logs/" },
      "Transitions": [
        { "Days": 30, "StorageClass": "GLACIER" }
      ],
      "Expiration": { "Days": 90 }
    }
  ]
}

5. Upgrade to Graviton (ARM)

When evaluating EC2 or RDS instance families, compare supported Graviton options against the workload's compatibility and performance requirements. Validate representative workloads before changing instance types.

Conclusion

Cost optimization is an ongoing practice. Review detached storage volumes, idle public IP allocations, development schedules, and storage lifecycle policies against workload and retention requirements. Measure the effect in your own account before treating a change as a saving.