AWS bills can spiral quickly—here's how to cut costs without sacrificing performance
Quick Wins (Week 1)
1. Find and Delete Unused Resources
Idle EC2 Instances:
# Find instances with low CPU utilization
aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 \
--metric-name CPUUtilization \
--dimensions Name=InstanceId,Value=i-xxxxx \
--statistics Average \
--period 86400 \
--start-time 2025-01-01T00:00:00Z \
--end-time 2025-01-31T00:00:00Z
Look for instances consistently under 5% CPU.
Unattached EBS Volumes:
aws ec2 describe-volumes \
--filters Name=status,Values=available \
--query 'Volumes[*].{ID:VolumeId,Size:Size,Type:VolumeType}'
Warning: Unattached volumes still cost money! A 100GB gp3 volume costs ~$8/month even when not attached.
Old Snapshots:
aws ec2 describe-snapshots --owner-ids self \
--query 'Snapshots[?StartTime<=`2024-01-01`].[SnapshotId,VolumeSize,StartTime]' \
--output table
| resource | monthlyCost | typicalSavings |
|---|---|---|
| Idle EC2 (t3.medium) | $30 | 100% |
| Unattached EBS (100GB) | $8 | 100% |
| Old Snapshots (500GB) | $25 | 100% |
| Unused Elastic IPs | $3.60 each | 100% |
2. Rightsize Your Instances
Most instances are oversized. Use AWS Cost Explorer's rightsizing recommendations:
# Get rightsizing recommendations via CLI
aws ce get-rightsizing-recommendation \
--service EC2 \
--configuration RecommendationTarget=SAME_INSTANCE_FAMILY
| environment | typicalOversize | recommendedAction |
|---|---|---|
| Production | 20-30% | Downsize one size |
| Development | 50-70% | Use smaller instances, stop overnight |
| Test/QA | 80%+ | Use spot instances, stop when unused |
3. Implement S3 Lifecycle Policies
{
"Rules": [
{
"ID": "TransitionToIA",
"Status": "Enabled",
"Filter": { "Prefix": "" },
"Transitions": [
{
"Days": 30,
"StorageClass": "STANDARD_IA"
},
{
"Days": 90,
"StorageClass": "GLACIER"
}
]
},
{
"ID": "DeleteOldVersions",
"Status": "Enabled",
"NoncurrentVersionExpiration": {
"NoncurrentDays": 30
}
}
]
}
| storageClass | costPerGB | useCase |
|---|---|---|
| S3 Standard | $0.023 | Frequently accessed data |
| S3 Infrequent Access | $0.0125 | Monthly access |
| S3 Glacier | $0.004 | Yearly access, archives |
| S3 Glacier Deep Archive | $0.00099 | Compliance, rare access |
Medium-Term Optimizations (Month 1-3)
4. Reserved Instances & Savings Plans
| commitment | discount | flexibility | bestFor |
|---|---|---|---|
| No Commitment | 0% | Full | Variable workloads |
| 1-Year Reserved | 30-40% | Limited | Stable baseline |
| 3-Year Reserved | 50-60% | Very Limited | Predictable long-term |
| Savings Plans | 20-40% | Moderate | Growing, flexible needs |
Strategy:
Reserved Instance Strategy:
Step 1: Analyze Usage
- Review 3+ months of usage data
- Identify stable baseline workloads
- Note peak vs average utilization
Step 2: Calculate Coverage
- Reserve 60-70% of baseline (safe)
- Keep 30-40% on-demand for flexibility
Step 3: Choose Commitment
- 1-year for uncertain growth
- 3-year for proven stable workloads
Example:
Baseline: 10 m5.xlarge instances
Reserved: 7 instances (70%)
On-Demand: 3 instances (30%)
Annual Savings: ~$15,000
5. Spot Instances for Non-Critical Workloads
# Spot Fleet configuration example
SpotFleetRequestConfig:
TargetCapacity: 10
TerminateInstancesWithExpiration: true
LaunchSpecifications:
- InstanceType: m5.xlarge
WeightedCapacity: 1
- InstanceType: m5.2xlarge
WeightedCapacity: 2
- InstanceType: m4.xlarge
WeightedCapacity: 1
AllocationStrategy: diversified
Spot Instance Best Practices
- • Use for batch processing, CI/CD, dev environments
- • Diversify across instance types and AZs
- • Handle interruption gracefully (2-minute warning)
- • Savings: 60-90% compared to on-demand
Advanced Optimizations
6. Auto-Scaling Based on Demand
# Auto-scaling policy example
ScalingPolicy:
TargetTrackingConfiguration:
TargetValue: 70.0
PredefinedMetricSpecification:
PredefinedMetricType: ASGAverageCPUUtilization
ScaleInCooldown: 300
ScaleOutCooldown: 60
# Scale to zero on weekends (dev environments)
ScheduledActions:
- ScheduledActionName: ScaleDownWeekend
Recurrence: "0 20 * * FRI"
MinSize: 0
MaxSize: 0
DesiredCapacity: 0
- ScheduledActionName: ScaleUpMonday
Recurrence: "0 6 * * MON"
MinSize: 2
MaxSize: 10
DesiredCapacity: 2
7. Use Graviton Processors
# Graviton instances offer 20-40% better price-performance
# Simply change instance type from:
# m5.xlarge → m6g.xlarge (ARM-based Graviton2)
# m5.xlarge → m7g.xlarge (ARM-based Graviton3)
| instance | hourlyPrice | performance |
|---|---|---|
| m5.xlarge (Intel) | $0.192 | Baseline |
| m6g.xlarge (Graviton2) | $0.154 | Similar or better |
| m7g.xlarge (Graviton3) | $0.163 | 25% better |
Cost Monitoring Setup
// Set up AWS Budget alerts
const budgetConfig = {
BudgetName: 'Monthly-AWS-Budget',
BudgetLimit: {
Amount: '5000',
Unit: 'USD'
},
BudgetType: 'COST',
TimeUnit: 'MONTHLY',
NotificationsWithSubscribers: [
{
Notification: {
NotificationType: 'ACTUAL',
ComparisonOperator: 'GREATER_THAN',
Threshold: 80,
ThresholdType: 'PERCENTAGE'
},
Subscribers: [
{
SubscriptionType: 'EMAIL',
Address: '[email protected]'
}
]
}
]
};
Summary: 40% Savings Breakdown
| optimization | effort | savings | timeline |
|---|---|---|---|
| Delete unused resources | Low | 10-15% | Week 1 |
| Rightsize instances | Medium | 15-25% | Week 2-3 |
| S3 lifecycle policies | Low | 5-10% | Week 1 |
| Reserved/Savings Plans | Medium | 30-40% | Month 1-2 |
| Spot instances | Medium | 60-90% (on applicable) | Month 2-3 |
Want a professional AWS cost audit?
We analyze your AWS infrastructure and identify optimization opportunities with specific savings estimates.
Get a Free Cost Audit