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 going to zero.
The generation tax
Previous-generation instance types are a silent killer. If you are running m4, t2, c4, or r4 instances, the current generation (m6i, t3, c6i, r6i) is 15-30% cheaper for the same or better performance. Graviton (ARM) variants like m6g or t4g save another 20% on top of that. 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.201/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 65% of their cost if nobody uses them outside business hours. An m5.large running 24/7 costs $70/month. Scheduled to run only during business hours (10hr/day, 5 days/week) brings it to $21/month. CostPatrol detects non-production instances by tag (env=dev, env=staging) 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 60-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 as Spot averages $0.03/hr = ~$22/month. That is 69% savings on every instance you move to Spot. 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 sub-minute granularity, basic monitoring is sufficient. On 50 instances, that is $105/month for data nobody looks at.
Fix it yourself
# Find idle instances (max CPU < 5% over 14 days)
aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 \
--metric-name CPUUtilization \
--dimensions Name=InstanceId,Value=i-0abc123 \
--start-time $(date -u -v-14d +%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