AWS Cost Optimization: Strategies for Reducing Cloud Spending
AWS costs can spiral out of control. After optimizing AWS spending for multiple organizations, here are strategies that reduce costs by 30-50%.
Cost Analysis
AWS Cost Explorer
# Enable Cost Explorer API
aws ce get-cost-and-usage \
--time-period Start=2019-01-01,End=2019-01-31 \
--granularity MONTHLY \
--metrics BlendedCost
Cost by Service
# Get costs by service
aws ce get-cost-and-usage \
--time-period Start=2019-01-01,End=2019-01-31 \
--granularity MONTHLY \
--metrics BlendedCost \
--group-by Type=SERVICE
EC2 Optimization
Right-Sizing Instances
# Use CloudWatch metrics to analyze
aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 \
--metric-name CPUUtilization \
--dimensions Name=InstanceId,Value=i-1234567890abcdef0 \
--start-time 2019-01-01T00:00:00Z \
--end-time 2019-01-31T23:59:59Z \
--period 3600 \
--statistics Average,Maximum
Right-sizing strategy:
- CPU < 20% → Downsize
- CPU > 80% → Upsize
- Memory < 20% → Use smaller instance
- Network < 10% → Use smaller instance
Reserved Instances
# Purchase Reserved Instances
aws ec2 purchase-reserved-instances-offering \
--reserved-instances-offering-id <offering-id> \
--instance-count 10 \
--limit-price Amount=1000.00,CurrencyCode=USD
Savings:
- 1-year term: 30-40% savings
- 3-year term: 50-60% savings
- Convertible: More flexibility, less savings
Spot Instances
# Request spot instances
aws ec2 request-spot-instances \
--spot-price "0.10" \
--instance-count 5 \
--type "one-time" \
--launch-specification file://specification.json
Use cases:
- Batch processing
- CI/CD workloads
- Data processing
- Testing environments
Auto Scaling
# Create auto scaling group
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name my-asg \
--min-size 2 \
--max-size 10 \
--desired-capacity 4 \
--launch-configuration-name my-launch-config \
--vpc-zone-identifier "subnet-123,subnet-456"
Benefits:
- Scale down during low traffic
- Scale up during peak
- 30-50% cost reduction
Storage Optimization
S3 Lifecycle Policies
{
"Rules": [
{
"Id": "Move to Glacier",
"Status": "Enabled",
"Transitions": [
{
"Days": 30,
"StorageClass": "STANDARD_IA"
},
{
"Days": 90,
"StorageClass": "GLACIER"
}
]
},
{
"Id": "Delete old versions",
"Status": "Enabled",
"NoncurrentVersionTransitions": [
{
"NoncurrentDays": 30,
"StorageClass": "GLACIER"
}
],
"NoncurrentVersionExpiration": {
"NoncurrentDays": 365
}
}
]
}
EBS Optimization
# Use gp3 instead of gp2
aws ec2 create-volume \
--volume-type gp3 \
--size 100 \
--iops 3000 \
--throughput 125
# Delete unused snapshots
aws ec2 describe-snapshots \
--owner-ids self \
--query 'Snapshots[?StartTime<`2019-01-01`].[SnapshotId,StartTime]' \
--output table
EBS Snapshot Cleanup
#!/bin/bash
# Delete snapshots older than 30 days
SNAPSHOTS=$(aws ec2 describe-snapshots \
--owner-ids self \
--query 'Snapshots[?StartTime<`'$(date -d '30 days ago' -u +%Y-%m-%dT%H:%M:%S)'`].SnapshotId' \
--output text)
for snapshot in $SNAPSHOTS; do
aws ec2 delete-snapshot --snapshot-id $snapshot
done
Database Optimization
RDS Right-Sizing
# Analyze RDS metrics
aws cloudwatch get-metric-statistics \
--namespace AWS/RDS \
--metric-name CPUUtilization \
--dimensions Name=DBInstanceIdentifier,Value=mydb \
--start-time 2019-01-01T00:00:00Z \
--end-time 2019-01-31T23:59:59Z \
--period 3600 \
--statistics Average,Maximum
Optimization:
- Use smaller instance types
- Enable auto-scaling
- Use read replicas for read-heavy workloads
DynamoDB Optimization
# Enable auto-scaling
aws application-autoscaling register-scalable-target \
--service-namespace dynamodb \
--resource-id table/my-table \
--scalable-dimension dynamodb:table:ReadCapacityUnits \
--min-capacity 5 \
--max-capacity 100
Cost reduction:
- Use on-demand billing for unpredictable workloads
- Use provisioned capacity for predictable workloads
- Enable TTL for automatic cleanup
Network Optimization
CloudFront Caching
# Create CloudFront distribution
aws cloudfront create-distribution \
--distribution-config file://cloudfront-config.json
Benefits:
- Reduced data transfer costs
- Lower latency
- Better user experience
VPC Endpoints
# Create VPC endpoint for S3
aws ec2 create-vpc-endpoint \
--vpc-id vpc-12345678 \
--service-name com.amazonaws.us-east-1.s3 \
--route-table-ids rtb-12345678
Savings:
- No data transfer charges
- Reduced NAT Gateway costs
Monitoring and Alerts
Cost Anomaly Detection
# Create cost anomaly detector
aws ce create-anomaly-detector \
--anomaly-detector-name cost-anomaly-detector \
--anomaly-detector-type DIMENSIONAL \
--monitor-dimension SERVICE
Budget Alerts
{
"BudgetName": "monthly-budget",
"BudgetLimit": {
"Amount": "1000",
"Unit": "USD"
},
"TimeUnit": "MONTHLY",
"BudgetType": "COST",
"CostFilters": {
"Service": ["Amazon Elastic Compute Cloud - Compute"]
},
"CalculatedSpend": {
"ActualSpend": {
"Amount": "0",
"Unit": "USD"
}
},
"NotificationsWithSubscribers": [
{
"Notification": {
"NotificationType": "ACTUAL",
"ComparisonOperator": "GREATER_THAN",
"Threshold": 80
},
"Subscribers": [
{
"SubscriptionType": "EMAIL",
"Address": "team@example.com"
}
]
}
]
}
Cost Optimization Checklist
Immediate Actions (Quick Wins)
- Delete unused EBS volumes
- Remove unattached elastic IPs
- Clean up old snapshots
- Delete unused load balancers
- Terminate stopped instances
- Remove unused security groups
Short-term (1-2 weeks)
- Right-size EC2 instances
- Purchase Reserved Instances
- Implement S3 lifecycle policies
- Enable auto-scaling
- Use spot instances for batch jobs
- Optimize RDS instance sizes
Long-term (1-3 months)
- Migrate to newer instance types
- Implement comprehensive monitoring
- Set up cost budgets and alerts
- Review and optimize architecture
- Use serverless where appropriate
- Implement cost allocation tags
Automation Scripts
Unused Resources Cleanup
import boto3
def cleanup_unused_resources():
ec2 = boto3.client('ec2')
# Find unattached volumes
volumes = ec2.describe_volumes(
Filters=[{'Name': 'status', 'Values': ['available']}]
)
for volume in volumes['Volumes']:
age = (datetime.now() - volume['CreateTime']).days
if age > 30:
print(f"Deleting unused volume: {volume['VolumeId']}")
ec2.delete_volume(VolumeId=volume['VolumeId'])
# Find unattached elastic IPs
addresses = ec2.describe_addresses()
for address in addresses['Addresses']:
if 'InstanceId' not in address:
print(f"Releasing unattached IP: {address['PublicIp']}")
ec2.release_address(AllocationId=address['AllocationId'])
Best Practices
- Monitor costs daily - Use Cost Explorer
- Set budgets - Alert on overspending
- Use tags - Track costs by project/team
- Right-size resources - Based on actual usage
- Use Reserved Instances - For predictable workloads
- Leverage spot instances - For flexible workloads
- Optimize storage - Lifecycle policies
- Review regularly - Monthly cost reviews
Expected Savings
- Right-sizing: 20-30% reduction
- Reserved Instances: 30-40% reduction
- Storage optimization: 40-60% reduction
- Auto-scaling: 30-50% reduction
- Overall: 30-50% total reduction
Conclusion
AWS cost optimization requires:
- Regular monitoring
- Right-sizing resources
- Using appropriate pricing models
- Automating cleanup
- Continuous review
Start with quick wins, then implement long-term strategies. The patterns shown here can reduce costs by 30-50%.
AWS cost optimization strategies from March 2019, covering practical cost reduction techniques.