Idle and oversized EC2 instances
The single biggest source of AWS waste. CostPatrol flags an EC2 instance as idle when its maximum CPU stays below 5% for 14 consecutive days. Oversized instances get flagged when average CPU is 5-20% and peak CPU never exceeds 50% over 14 days.
Idle instances should be stopped or terminated. Oversized instances should be downsized one step. An m5.xlarge at $0.192/hr downsized to m5.large at $0.096/hr saves $70/month. If the instance is completely idle, that is $140/month in instance hours going to zero.
The generation tax
Previous-generation instance types cost more than they look. If you are running m4, t2, c4, or r4 instances, the current generation (m6i, t3, c6i, r6i) is only 4-15% cheaper per hour. Most of the gain is performance per dollar, not the hourly price. Graviton (ARM) variants like m6g or t4g cost about 20% less again. A common upgrade path:
- m4.large ($0.10/hr) to m6i.large ($0.096/hr) to m6g.large ($0.077/hr) = 23% total savings
- t2.medium ($0.0464/hr) to t3.medium ($0.0416/hr) to t4g.medium ($0.0336/hr) = 28% total savings
- r4.xlarge ($0.266/hr) to r6i.xlarge ($0.252/hr) to r6g.xlarge ($0.2016/hr) = 24% total savings
There is no performance penalty going from Intel to Graviton for most workloads. The exceptions are applications that depend on x86-specific instructions, certain commercial databases, or Windows. Everything else (Linux, containers, web servers, APIs) runs the same or faster.
Non-production scheduling
Development and staging instances that run 24/7 waste about 65% of their compute cost if nobody uses them outside business hours. An m5.large running 24/7 costs $70/month. Scheduled to run only 12 hours a day, 5 days a week, it costs about $25/month. CostPatrol spots non-production instances by their environment tag (env=dev, env=staging and similar) or their name, and flags the savings opportunity. AWS Instance Scheduler or a simple EventBridge rule with a Lambda function handles this.
Spot Instances for fault-tolerant workloads
Spot Instances cost up to 90% less than On-Demand. The tradeoff: AWS can reclaim them with 2 minutes notice. This is fine for batch processing, CI/CD runners, data pipelines, worker queues, and any workload that can handle interruption. An m5.large On-Demand at $0.096/hr costs ~$70/month. The same instance on Spot was $0.0397/hr on 12 October 2026, about $29/month. That is 59% less, and the Spot price moves with demand. Use Spot Fleet or EC2 Auto Scaling with mixed instance policies to maintain capacity across instance types and AZs.
Detailed monitoring: $2.10/month you probably do not need
EC2 Detailed Monitoring sends metrics at 1-minute intervals instead of the default 5-minute. It costs $2.10/month per instance. Unless you are actively debugging or running auto-scaling that needs 1-minute granularity, basic monitoring is enough. On 50 instances, that is $105/month for data nobody looks at.
Fix it yourself
# Find idle instances (max CPU < 5% over 14 days)
# GNU date (Linux, CloudShell). On macOS use: date -u -v-14d +%Y-%m-%dT%H:%M:%S
aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 \
--metric-name CPUUtilization \
--dimensions Name=InstanceId,Value=i-0abc123 \
--start-time $(date -u -d '-14 days' +%Y-%m-%dT%H:%M:%S) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
--period 3600 --statistics Maximum
# Stop idle instance
aws ec2 stop-instances --instance-ids i-0abc123
# Downsize: change instance type (stop first)
aws ec2 stop-instances --instance-ids i-0abc123
aws ec2 modify-instance-attribute \
--instance-id i-0abc123 \
--instance-type m5.large
aws ec2 start-instances --instance-ids i-0abc123
# Find all previous-generation instances
aws ec2 describe-instances \
--query 'Reservations[].Instances[?starts_with(InstanceType, `m4`) || starts_with(InstanceType, `t2`) || starts_with(InstanceType, `c4`) || starts_with(InstanceType, `r4`)].{ID:InstanceId,Type:InstanceType,State:State.Name}' \
--output table
# Disable detailed monitoring
aws ec2 unmonitor-instances --instance-ids i-0abc123