· 15 min read

AWS Cost Optimization Checklist: 50 Items Every Team Should Check

This checklist covers 50 specific AWS cost optimization checks you can run today. Every item includes an AWS CLI command, what to look for, and the typical savings. These checks are derived from real-world optimization patterns across hundreds of AWS accounts.

CostPatrol automates most of these checks. If you’d rather not run 50 CLI commands manually, a free scan covers the majority of these items in minutes.

EC2 (10 Items)

1. Find idle EC2 instances

Instances with CPU utilization consistently below 5% over 7 days are likely idle and should be terminated or stopped.

aws cloudwatch get-metric-statistics \
  --namespace AWS/EC2 \
  --metric-name CPUUtilization \
  --dimensions Name=InstanceId,Value=i-0abc123def456 \
  --start-time $(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 86400 --statistics Maximum \
  --query 'Datapoints[].Maximum'

Rule ID: EC2-O002 | Savings: 100% of instance cost

2. Identify previous-generation instance types

Instances running m4, c4, r4, t2, m3, c3, or r3 families are 20-40% more expensive per unit of compute than current-generation equivalents.

aws ec2 describe-instances \
  --query 'Reservations[].Instances[?State.Name==`running`].{ID:InstanceId,Type:InstanceType}' \
  --output table | grep -E 'm[34]\.|c[34]\.|r[34]\.|t2\.'

Rule ID: EC2-O001 | Savings: 10-30% per instance

3. Find stopped instances with attached EBS volumes

Stopped instances don’t incur compute charges, but their EBS volumes keep billing. A stopped m5.xlarge with 500 GB gp3 costs $40/month in storage alone.

aws ec2 describe-instances \
  --filters Name=instance-state-name,Values=stopped \
  --query 'Reservations[].Instances[].{ID:InstanceId,Volumes:BlockDeviceMappings[].Ebs.VolumeId}' \
  --output table

Rule ID: OPT-022 | Savings: 100% of attached EBS cost

4. Check for on-demand instances without Savings Plans coverage

Instances running 500+ hours/month on-demand are candidates for Savings Plans (30-60% discount).

aws ce get-savings-plans-coverage \
  --time-period Start=2026-02-01,End=2026-03-01 \
  --granularity MONTHLY \
  --query 'SavingsPlansCoverages[].{Coverage:Coverage.CoveragePercentage}'

Rule ID: EC2-004 | Savings: 30-60% on covered instances

5. Identify oversized EC2 instances

Instances with peak CPU below 30% and peak memory below 40% over 14 days can likely be downsized.

aws cloudwatch get-metric-statistics \
  --namespace AWS/EC2 \
  --metric-name CPUUtilization \
  --dimensions Name=InstanceId,Value=i-0abc123def456 \
  --start-time $(date -u -d '14 days ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 604800 --statistics Maximum \
  --query 'Datapoints[].Maximum'

Rule ID: OPT-023 | Savings: 30-60% by downsizing

6. Find instances not using Graviton (ARM)

Graviton instances (m7g, c7g, r7g) offer 20-40% better price-performance than x86 equivalents for most workloads.

aws ec2 describe-instances \
  --filters Name=instance-state-name,Values=running \
  --query 'Reservations[].Instances[?!contains(InstanceType, `g.`) && !contains(InstanceType, `gd.`)].{ID:InstanceId,Type:InstanceType}' \
  --output table

Rule ID: EC2-O003 | Savings: 20-40% per instance

7. Check for instances with Detailed Monitoring enabled unnecessarily

Detailed Monitoring ($2.10/instance/month) provides 1-minute metrics. Most workloads are fine with 5-minute (free) metrics.

aws ec2 describe-instances \
  --filters Name=monitoring-state,Values=enabled \
  --query 'Reservations[].Instances[].{ID:InstanceId,Type:InstanceType}' \
  --output table

Rule ID: OPT-021 | Savings: $2.10/instance/month

8. Find instances running in non-production during off-hours

Dev/staging instances running 24/7 when they’re only needed during business hours waste 65-70% of compute costs.

aws ec2 describe-instances \
  --filters Name=instance-state-name,Values=running \
  --query 'Reservations[].Instances[].{ID:InstanceId,Type:InstanceType,Tags:Tags[?Key==`Environment`].Value|[0]}' \
  --output table | grep -iE 'dev|staging|test'

Rule ID: EC2-O004 | Savings: 65-70% with scheduling

9. Check for orphaned Elastic IPs

Unattached Elastic IPs cost $3.60/month each since the AWS public IPv4 pricing change.

aws ec2 describe-addresses \
  --query 'Addresses[?AssociationId==null].{IP:PublicIp,AllocID:AllocationId}' \
  --output table

Rule ID: EIP-O001 | Savings: $3.60/IP/month

10. Find EIPs on stopped instances

Elastic IPs attached to stopped instances also incur the idle charge.

aws ec2 describe-addresses \
  --query 'Addresses[?AssociationId!=null].{IP:PublicIp,InstanceId:InstanceId}' \
  --output text | while read ip inst; do
  state=$(aws ec2 describe-instances --instance-ids $inst \
    --query 'Reservations[0].Instances[0].State.Name' --output text 2>/dev/null)
  [ "$state" = "stopped" ] && echo "EIP $ip on stopped instance $inst"
done

Rule ID: EIP-O002 | Savings: $3.60/IP/month

RDS (8 Items)

11. Find idle RDS instances (zero connections)

RDS instances with zero database connections for 14+ days are idle and should be snapshotted and deleted.

aws cloudwatch get-metric-statistics \
  --namespace AWS/RDS \
  --metric-name DatabaseConnections \
  --dimensions Name=DBInstanceIdentifier,Value=my-database \
  --start-time $(date -u -d '14 days ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 86400 --statistics Maximum \
  --query 'Datapoints[].Maximum'

Rule ID: RDS-O001 | Savings: 100% of instance cost

12. Check for Multi-AZ on non-production databases

Multi-AZ doubles your RDS cost. Non-production databases rarely need it.

aws rds describe-db-instances \
  --query 'DBInstances[?MultiAZ==`true`].{ID:DBInstanceIdentifier,Class:DBInstanceClass,Tags:TagList[?Key==`Environment`].Value|[0]}' \
  --output table

Rule ID: RDS-O004 | Savings: 50% of instance cost

13. Find RDS instances without Reserved Instance coverage

Production RDS instances running on-demand for 12+ months should have RI coverage.

aws rds describe-reserved-db-instances \
  --query 'ReservedDBInstances[?State==`active`].{Class:DBInstanceClass,Count:DBInstanceCount,End:StartTime}' \
  --output table

Savings: 30-40% with 1-year no-upfront RI

14. Identify oversized RDS instances

Check CPU and connection metrics to find databases that could run on a smaller instance class.

aws cloudwatch get-metric-statistics \
  --namespace AWS/RDS \
  --metric-name CPUUtilization \
  --dimensions Name=DBInstanceIdentifier,Value=my-database \
  --start-time $(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 86400 --statistics Maximum \
  --query 'Datapoints[].Maximum'

Rule ID: RDS-O007 | Savings: 30-60% by downsizing

15. Find unused RDS read replicas

Read replicas with zero read traffic are pure waste.

aws rds describe-db-instances \
  --query 'DBInstances[?ReadReplicaSourceDBInstanceIdentifier!=null].{Replica:DBInstanceIdentifier,Source:ReadReplicaSourceDBInstanceIdentifier,Class:DBInstanceClass}' \
  --output table

Rule ID: OPT-024 | Savings: 100% of replica cost

16. Check RDS backup retention overage

RDS provides free backup storage equal to your provisioned database storage. Backup exceeding this amount is charged at $0.095/GB/month.

aws rds describe-db-instances \
  --query 'DBInstances[].{ID:DBInstanceIdentifier,Storage:AllocatedStorage,BackupRetention:BackupRetentionPeriod}' \
  --output table

Rule ID: RDS-O003 | Savings: 20-100% of backup overage

17. Find RDS instances on GP2 storage

GP3 provides 20% cost savings over GP2 with better baseline performance (3,000 IOPS vs 100 IOPS/GB).

aws rds describe-db-instances \
  --query 'DBInstances[?StorageType==`gp2`].{ID:DBInstanceIdentifier,Size:AllocatedStorage,Type:StorageType}' \
  --output table

Rule ID: RDS-O006 | Savings: 20% on storage costs

18. Detect RDS cluster sprawl

Multiple RDS clusters in the same account with similar engines often indicate forgotten test databases or duplicated environments.

aws rds describe-db-instances \
  --query 'DBInstances[].{ID:DBInstanceIdentifier,Engine:Engine,Class:DBInstanceClass,Status:DBInstanceStatus}' \
  --output table

Rule ID: RDS-O005 | Savings: 70-100% per unnecessary cluster

Lambda (6 Items)

19. Find unused Lambda functions (zero invocations)

Functions with zero invocations in 30 days should be deleted.

# Check invocations for a specific function
aws cloudwatch get-metric-statistics \
  --namespace AWS/Lambda \
  --metric-name Invocations \
  --dimensions Name=FunctionName,Value=my-function \
  --start-time $(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 2592000 --statistics Sum \
  --query 'Datapoints[0].Sum'

Rule ID: LAM-O003 | Savings: 100% (small per function, significant at scale)

20. Identify over-provisioned Lambda memory

Functions using less than 50% of allocated memory are over-provisioned.

aws logs filter-log-events \
  --log-group-name /aws/lambda/my-function \
  --filter-pattern "REPORT" \
  --start-time $(date -u -d '7 days ago' +%s)000 \
  --query 'events[].message' --output text | \
  grep -oP 'Memory Size: \K[0-9]+|Max Memory Used: \K[0-9]+'

Rule ID: LAM-O002 | Savings: 10-40% per function

21. Find x86 Lambda functions eligible for ARM64

ARM64 (Graviton2) provides 20% cost savings for compatible runtimes.

aws lambda list-functions \
  --query 'Functions[?Architectures[0]!=`arm64` && (Runtime==`nodejs20.x` || Runtime==`python3.12` || Runtime==`python3.13`)].{Name:FunctionName,Runtime:Runtime}' \
  --output table

Rule ID: LAM-O001 | Savings: 20% per function

22. Check for Lambda functions with excessive timeouts

Functions with 900-second timeouts that average under 10 seconds of execution are misconfigured.

aws lambda list-functions \
  --query 'Functions[?Timeout>`300`].{Name:FunctionName,Timeout:Timeout}' \
  --output table

Rule ID: LAM-O004 | Savings: Risk reduction (prevents runaway billing)

23. Detect Lambda cost spikes (duration anomalies)

Compare recent Lambda costs against the 14-day baseline to catch spikes.

aws ce get-cost-and-usage \
  --time-period Start=2026-03-12,End=2026-03-13 \
  --granularity DAILY \
  --metrics UnblendedCost \
  --filter '{"Dimensions":{"Key":"SERVICE","Values":["AWS Lambda"]}}' \
  --query 'ResultsByTime[0].Total.UnblendedCost.Amount'

Rule ID: LAM-A001 | Trigger: 200% deviation from 14-day rolling average

24. Check for Lambda functions on deprecated runtimes

Functions on end-of-life runtimes (Node.js 16.x, Python 3.8) don’t get security patches and may have performance regressions.

aws lambda list-functions \
  --query 'Functions[?Runtime==`nodejs16.x` || Runtime==`python3.8` || Runtime==`python3.9`].{Name:FunctionName,Runtime:Runtime}' \
  --output table

Savings: Indirect (security risk reduction + potential performance improvement)

EBS (6 Items)

25. Find unattached EBS volumes

Unattached volumes in “available” state are pure waste.

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

Rule ID: EBS-O002 | Savings: 100% of volume cost

26. Identify GP2 volumes for GP3 migration

GP3 is 20% cheaper than GP2 with better baseline performance.

aws ec2 describe-volumes \
  --filters Name=volume-type,Values=gp2 \
  --query 'Volumes[].{ID:VolumeId,Size:Size,IOPS:Iops}' \
  --output table

Rule ID: EBS-O001 | Savings: 20% per volume

27. Check for over-provisioned GP3 IOPS/throughput

GP3 includes 3,000 IOPS and 125 MB/s free. Provisioned IOPS beyond this cost extra. Check if you’re actually using them.

aws cloudwatch get-metric-statistics \
  --namespace AWS/EBS \
  --metric-name VolumeReadOps \
  --dimensions Name=VolumeId,Value=vol-0abc123 \
  --start-time $(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 86400 --statistics Maximum

Rule ID: EBS-O005 | Savings: 10-70% of performance add-on cost

28. Find idle Fast Snapshot Restore (FSR)

FSR charges $0.75/AZ/hour for each snapshot-AZ combination. Unused FSR adds up fast.

aws ec2 describe-fast-snapshot-restores \
  --query 'FastSnapshotRestores[?State==`enabled`].{Snapshot:SnapshotId,AZ:AvailabilityZone}' \
  --output table

Rule ID: EBS-O004 | Savings: $0.75/AZ/hour ($540/AZ/month)

29. Check for stale EBS snapshots

Snapshots from deleted volumes or old backups that are no longer needed.

aws ec2 describe-snapshots --owner-ids self \
  --query 'sort_by(Snapshots, &StartTime)[:20].{ID:SnapshotId,Size:VolumeSize,Date:StartTime,Desc:Description}' \
  --output table

Rule ID: EBS-O006 | Savings: $0.05/GB/month

30. Find EBS snapshot archiving opportunities

Snapshots accessed less than once per 90 days can be moved to archive tier for 75% savings.

aws ec2 describe-snapshots --owner-ids self \
  --query 'Snapshots[?StorageTier!=`archive`].{ID:SnapshotId,Size:VolumeSize,Created:StartTime}' \
  --output table

Rule ID: EBS-O003 | Savings: 75% on snapshot storage

S3 (5 Items)

31. Find buckets without lifecycle policies

Large buckets without lifecycle rules accumulate data indefinitely.

for bucket in $(aws s3api list-buckets --query 'Buckets[].Name' --output text); do
  rules=$(aws s3api get-bucket-lifecycle-configuration --bucket $bucket 2>/dev/null | \
    grep -c '"ID"' || echo 0)
  [ "$rules" = "0" ] && echo "No lifecycle: $bucket"
done

Rule ID: S3-O001 | Savings: 40-70% with intelligent tiering

32. Check for incomplete multipart uploads

Failed multipart uploads leave orphaned parts that incur storage charges indefinitely.

aws s3api list-multipart-uploads --bucket my-bucket \
  --query 'Uploads[].{Key:Key,Initiated:Initiated}' \
  --output table

Rule ID: OPT-017 | Savings: Varies (can be substantial for large uploads)

33. Find S3 versioned buckets with noncurrent version bloat

Versioned buckets without noncurrent version expiration accumulate old versions forever.

aws s3api get-bucket-versioning --bucket my-bucket
# If enabled, check for noncurrent version lifecycle rules
aws s3api get-bucket-lifecycle-configuration --bucket my-bucket \
  --query 'Rules[?NoncurrentVersionExpiration!=null]'

Rule ID: S3-O003 | Savings: 20-80% by expiring old versions

34. Check for Standard storage on infrequently accessed data

Buckets with low request rates on Standard storage should use S3 Intelligent-Tiering or S3-IA.

aws cloudwatch get-metric-statistics \
  --namespace AWS/S3 \
  --metric-name AllRequests \
  --dimensions Name=BucketName,Value=my-bucket Name=FilterId,Value=EntireBucket \
  --start-time $(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 2592000 --statistics Sum

Rule ID: S3-003 | Savings: 40-68% with Intelligent-Tiering

35. Find empty S3 buckets

Empty buckets incur no storage costs but add operational clutter. Clean them up.

for bucket in $(aws s3api list-buckets --query 'Buckets[].Name' --output text); do
  count=$(aws s3api list-objects-v2 --bucket $bucket --max-items 1 \
    --query 'KeyCount' --output text 2>/dev/null)
  [ "$count" = "0" ] && echo "Empty: $bucket"
done

Rule ID: S3-O004 | Savings: Minimal (operational hygiene)

DynamoDB (5 Items)

36. Find over-provisioned DynamoDB tables

Tables using less than 30% of provisioned RCU/WCU are over-provisioned.

aws dynamodb list-tables --query 'TableNames' --output text | tr '\t' '\n' | while read table; do
  mode=$(aws dynamodb describe-table --table-name $table \
    --query 'Table.BillingModeSummary.BillingMode' --output text 2>/dev/null)
  [ "$mode" = "PROVISIONED" ] && echo "Provisioned: $table"
done

Rule ID: DDB-001 | Savings: 30-70% by right-sizing or switching to on-demand

37. Identify on-demand tables suitable for provisioned capacity

Tables with steady, predictable traffic patterns are cheaper on provisioned with auto-scaling.

aws dynamodb describe-table --table-name my-table \
  --query 'Table.{Name:TableName,Mode:BillingModeSummary.BillingMode,Items:ItemCount,Size:TableSizeBytes}'

Rule ID: DDB-002 | Savings: 20-40% for steady workloads

38. Find unused DynamoDB tables

Tables with zero reads and writes for 14+ days are candidates for deletion.

aws cloudwatch get-metric-statistics \
  --namespace AWS/DynamoDB \
  --metric-name ConsumedReadCapacityUnits \
  --dimensions Name=TableName,Value=my-table \
  --start-time $(date -u -d '14 days ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 1209600 --statistics Sum \
  --query 'Datapoints[0].Sum'

Rule ID: DDB-003 | Savings: 100% of table cost

39. Find unused Global Secondary Indexes

GSIs that are never queried consume write capacity and storage.

aws dynamodb describe-table --table-name my-table \
  --query 'Table.GlobalSecondaryIndexes[].{Name:IndexName,Status:IndexStatus,Items:ItemCount}'

Rule ID: DDB-004 | Savings: 100% of GSI cost

40. Check for Standard-IA table class opportunities

Tables with storage costs exceeding 2x their read/write costs should use Standard-IA (60% cheaper storage, 25% more expensive reads/writes).

aws dynamodb describe-table --table-name my-table \
  --query 'Table.{Name:TableName,Class:TableClassSummary.TableClass,SizeGB:TableSizeBytes}'

Rule ID: DDB-005 | Savings: 40-60% for storage-heavy tables

CloudWatch (5 Items)

41. Find log groups without retention policies

Log groups without retention accumulate data forever at $0.03/GB/month.

aws logs describe-log-groups \
  --query 'logGroups[?retentionInDays==null].{Name:logGroupName,SizeBytes:storedBytes}' \
  --output table

Rule ID: CWL-O001 | Savings: 50-80% of log storage costs

42. Identify the most expensive log groups

Find which log groups are consuming the most storage and ingestion.

aws logs describe-log-groups \
  --query 'sort_by(logGroups, &storedBytes)[-10:].{Name:logGroupName,StoredGB:storedBytes}' \
  --output table

Savings: Targeted retention policies on top consumers

43. Check for duplicate CloudTrail trails

Multiple CloudTrail trails in the same region deliver duplicate logs and charges.

aws cloudtrail describe-trails \
  --query 'trailList[].{Name:Name,Region:HomeRegion,IsMultiRegion:IsMultiRegionTrail,IsOrgTrail:IsOrganizationTrail}' \
  --output table

Rule ID: CT-O001 | Savings: 100% per duplicate trail

44. Find orphaned CloudWatch alarms

Alarms referencing deleted resources cost $0.10/alarm/month and create operational noise.

aws cloudwatch describe-alarms \
  --state-value INSUFFICIENT_DATA \
  --query 'MetricAlarms[].{Name:AlarmName,Metric:MetricName,Namespace:Namespace}' \
  --output table

Rule ID: CW-O002 | Savings: $0.10/alarm/month + noise reduction

45. Check for Lambda dual-write logging

Lambda functions that send logs to both CloudWatch and an external APM (Datadog, New Relic) are double-paying for log ingestion.

aws logs describe-log-groups \
  --log-group-name-prefix /aws/lambda/ \
  --query 'logGroups[?subscriptionFilters!=null].{Name:logGroupName,Subscriptions:subscriptionFilters}' \
  --output table 2>/dev/null || echo "Check subscription filters manually"

Rule ID: CWL-O002 | Savings: 100% of CW ingestion cost if external APM captures the same data

NAT Gateway (3 Items)

46. Identify NAT Gateway traffic eligible for VPC Endpoints

S3 and DynamoDB Gateway Endpoints are free and eliminate NAT Gateway data processing charges for those services.

# Check if S3 gateway endpoint exists
aws ec2 describe-vpc-endpoints \
  --filters Name=service-name,Values=com.amazonaws.*.s3 Name=vpc-endpoint-type,Values=Gateway \
  --query 'VpcEndpoints[].{VpcId:VpcId,State:State}' \
  --output table

Rule ID: NAT-O001 | Savings: 50-90% of NAT data processing costs

47. Check NAT Gateway data processing costs

NAT Gateway charges $0.045/GB of data processed. High-traffic applications can rack up significant costs.

aws ce get-cost-and-usage \
  --time-period Start=2026-02-01,End=2026-03-01 \
  --granularity MONTHLY \
  --metrics UnblendedCost \
  --filter '{"Dimensions":{"Key":"SERVICE","Values":["Amazon Virtual Private Cloud"]}}' \
  --query 'ResultsByTime[0].Total.UnblendedCost.Amount'

Savings: Varies based on traffic patterns

48. Find redundant NAT Gateways

Non-production VPCs with NAT Gateways in multiple AZs may only need one.

aws ec2 describe-nat-gateways \
  --filter Name=state,Values=available \
  --query 'NatGateways[].{ID:NatGatewayId,VpcId:VpcId,SubnetId:SubnetId,AZ:SubnetId}' \
  --output table

Rule ID: NAT-O002 | Savings: ~50% by consolidating to single AZ (non-production only)

General (2 Items)

49. Check for AWS Config in unnecessary regions

AWS Config charges per configuration item recorded. If enabled in all 20+ regions but you only use 2-3, you’re paying for configuration recordings in unused regions.

for region in $(aws ec2 describe-regions --query 'Regions[].RegionName' --output text); do
  status=$(aws configservice describe-configuration-recorder-status \
    --region $region --query 'ConfigurationRecordersStatus[0].recording' \
    --output text 2>/dev/null)
  [ "$status" = "True" ] && echo "Config active in: $region"
done

Rule ID: CFG-O001 | Savings: $100-$1,000+/month

50. Review overall Savings Plans utilization

Low Savings Plans utilization means you’re over-committed. High utilization with significant on-demand spend means you could buy more.

aws ce get-savings-plans-utilization \
  --time-period Start=2026-02-01,End=2026-03-01 \
  --query 'Total.{Utilization:UtilizationPercentage,Used:UsedCommitment,Unused:UnusedCommitment}'

Target: 90-95% utilization. Below 85% suggests over-commitment. 100% with on-demand spend suggests room for additional coverage.

How to Use This Checklist

Running all 50 checks manually takes hours. Here’s a practical approach:

Quick wins (30 minutes): Run items 9, 25, 26, 31, and 41 first. These are the easiest to check and often yield immediate savings (orphaned EIPs, unattached volumes, GP2-to-GP3, missing lifecycle policies, missing log retention).

Deep dive (2-3 hours): Work through EC2 (items 1-5), RDS (items 11-12), and DynamoDB (items 36-40) to find idle and oversized resources.

Recurring review (monthly): Re-run items 4, 19, 38, 46, and 50 monthly to catch new idle resources and verify commitment coverage.

Or automate it: CostPatrol runs the equivalent of this entire checklist in a single scan. It connects to your AWS account via a read-only CloudFormation role, evaluates 60+ detection rules across all services, and produces a prioritized report with estimated savings for each finding. The free scan covers most items on this list.

Expected Savings by Category

CategoryTypical Monthly SavingsEffort Level
EC2 idle/oversized$200-$2,000Medium
EBS cleanup$50-$500Low
RDS right-sizing$100-$1,000Medium
Lambda optimization$50-$500Low
S3 lifecycle policies$100-$1,000Low
DynamoDB right-sizing$50-$500Medium
CloudWatch log retention$50-$300Low
NAT Gateway optimization$100-$2,000Medium
Commitment coverage$500-$5,000Low

For a team spending $50,000/month on AWS, working through this checklist typically identifies $5,000-$15,000 in monthly savings (10-30%). The exact amount depends on how much optimization has already been done.

See what CostPatrol finds in your AWS account

Free scan shows your total savings. Upgrade to Pro for full findings, fix commands, and daily Slack alerts.