Skip to content
Vladimir Chavkov
Go back

AWS Cost Optimization Strategies: Complete Guide

Edit page

AWS Cost Optimization Strategies: Complete Guide

AWS cost optimization is a critical discipline for organizations looking to maximize their cloud investment while maintaining performance and reliability. With AWS’s vast array of services and pricing models, understanding and implementing effective cost optimization strategies can lead to significant savings—often 20-40% or more of total cloud spending. This comprehensive guide covers proven strategies, tools, and best practices for optimizing your AWS costs.

Understanding AWS Cost Structure

AWS Pricing Components

  1. Compute Costs: EC2 instances, Lambda functions, ECS/EKS containers
  2. Storage Costs: S3, EBS, EFS, Glacier storage classes
  3. Data Transfer: Inter-AZ, cross-region, internet egress
  4. Database Costs: RDS, DynamoDB, Redshift instances
  5. Network Costs: VPC endpoints, Direct Connect, CloudFront
  6. Support Costs: AWS Support plans (Basic, Developer, Business, Enterprise)

Cost Allocation Tags

{
"Environment": "production|staging|development",
"Project": "project-name",
"Owner": "team-name",
"CostCenter": "department-code",
"Application": "app-name"
}

Compute Cost Optimization

1. Rightsizing EC2 Instances

Instance Analysis Tools

Terminal window
# AWS Compute Optimizer CLI
aws compute-optimizer get-recommendation-summaries \
--service-ec2 \
--query 'RecommendationSummaries[0].Summaries'
# CloudWatch metrics for utilization
aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 \
--metric-name CPUUtilization \
--dimensions Name=InstanceId,Value=i-1234567890abcdef0 \
--statistics Average \
--period 3600 \
--start-time 2024-01-01T00:00:00Z \
--end-time 2024-01-31T23:59:59Z

Rightsizing Best Practices

  1. Monitor Utilization: Track CPU, memory, network, and disk I/O
  2. Use Compute Optimizer: Leverage AWS’s ML-based recommendations
  3. Consider Burstable Instances: T3/T4 instances for variable workloads
  4. Evaluate Graviton Processors: Up to 40% cost savings for compatible workloads

2. Reserved Instances (RIs)

RI Types and Benefits

Standard Reserved Instances:
- Up to 72% savings vs On-Demand
- 1-year or 3-year terms
- No upfront, partial upfront, or all upfront payment options
- Flexible within instance family
Convertible Reserved Instances:
- Up to 54% savings vs On-Demand
- Can change instance types, OS, or tenancy
- 1-year or 3-year terms
- Partial or all upfront payment options
Scheduled Reserved Instances:
- For recurring workloads
- Purchase capacity for specific time windows
- Up to 10% savings vs On-Demand

RI Purchase Strategy

import boto3
import json
def purchase_reserved_instances(instance_type, term, payment_option):
client = boto3.client('ec2')
response = client.purchase_reserved_instances_offering(
ReservedInstancesOfferingId='rio-12345678',
InstanceCount=1,
InstanceType=instance_type,
AvailabilityZone='us-east-1a',
Term=term,
PurchaseTime=datetime.datetime.now(),
PaymentOption=payment_option
)
return response
# Example: Purchase 3-year all-upfront RI
purchase_reserved_instances(
instance_type='t3.medium',
term='3years',
payment_option='AllUpfront'
)

3. Savings Plans

Compute Savings Plans

Compute Savings Plans:
- Up to 66% savings vs On-Demand
- Flexible across instance families, sizes, regions
- 1-year or 3-year commitments
- Applies to EC2, Fargate, Lambda
Usage Example:
- $10/hour commitment
- Can use any combination of compute services
- Automatic coverage across eligible usage

EC2 Instance Savings Plans

EC2 Instance Savings Plans:
- Up to 72% savings vs On-Demand
- Fixed to instance family within a region
- 1-year or 3-year commitments
- More restrictive but higher savings

4. Spot Instances

Spot Instance Strategy

import boto3
def launch_spot_instance():
client = boto3.client('ec2')
response = client.request_spot_instances(
SpotPrice='0.02',
InstanceCount=1,
Type='one-time',
LaunchSpecification={
'ImageId': 'ami-12345678',
'InstanceType': 't3.medium',
'KeyName': 'my-key-pair',
'SecurityGroupIds': ['sg-12345678'],
'SubnetId': 'subnet-12345678',
'IamInstanceProfile': {
'Name': 'ec2-spot-role'
}
}
)
return response
# Spot Fleet configuration
spot_fleet_config = {
'SpotFleetRequestConfig': {
'SpotPrice': '0.02',
'TargetCapacity': 10,
'IamFleetRole': 'arn:aws:iam::123456789012:role/aws-ec2-spot-fleet-tagging-role',
'LaunchSpecifications': [
{
'ImageId': 'ami-12345678',
'InstanceType': 't3.medium',
'SubnetId': 'subnet-12345678'
}
]
}
}

Spot Instance Best Practices

  1. Use Spot Fleets: Diversify across instance types and AZs
  2. Implement Graceful Shutdown: Handle interruption notices
  3. Use Checkpointing: Save work progress frequently
  4. Combine with Auto Scaling: Maintain desired capacity

Storage Cost Optimization

1. S3 Storage Classes

S3 Storage Class Selection

S3 Standard:
- Frequently accessed data
- Millisecond latency
- $0.023 per GB (first 50 TB/month)
S3 Standard-IA:
- Infrequently accessed data
- Millisecond latency
- $0.0125 per GB + $0.01 per 1,000 retrievals
S3 One Zone-IA:
- Infrequently accessed, non-critical data
- Millisecond latency
- $0.01 per GB + $0.01 per 1,000 retrievals
S3 Glacier:
- Long-term archival
- Minutes to hours retrieval
- $0.004 per GB + retrieval costs
S3 Glacier Deep Archive:
- Long-term archival, rarely accessed
- Hours to days retrieval
- $0.00099 per GB + retrieval costs

S3 Lifecycle Policies

{
"Rules": [
{
"ID": "DataLifecycle",
"Status": "Enabled",
"Filter": {
"Prefix": "logs/"
},
"Transitions": [
{
"Days": 30,
"StorageClass": "STANDARD_IA"
},
{
"Days": 90,
"StorageClass": "GLACIER"
},
{
"Days": 365,
"StorageClass": "DEEP_ARCHIVE"
}
],
"Expiration": {
"Days": 2555
}
}
]
}

2. EBS Volume Optimization

EBS Volume Types

gp3 (General Purpose SSD):
- Baseline performance: 3,000 IOPS, 125 MB/s
- Up to 16,000 IOPS, 1,000 MB/s
- Cost-effective for most workloads
io2 Block Express:
- Highest performance storage
- Up to 256,000 IOPS, 4,000 MB/s
- Sub-millisecond latency
- Higher cost but better performance
st1 (Throughput Optimized HDD):
- Large sequential workloads
- Up to 500 MB/s throughput
- Low cost per GB
sc1 (Cold HDD):
- Infrequently accessed data
- Up to 250 MB/s throughput
- Lowest cost per GB

EBS Optimization Script

import boto3
def optimize_ebs_volumes():
client = boto3.client('ec2')
# Get all volumes
volumes = client.describe_volumes()
for volume in volumes['Volumes']:
volume_id = volume['VolumeId']
volume_type = volume['VolumeType']
size_gb = volume['Size']
# Check for underutilized volumes
cloudwatch = boto3.client('cloudwatch')
metrics = cloudwatch.get_metric_statistics(
Namespace='AWS/EBS',
MetricName='VolumeReadOps',
Dimensions=[
{
'Name': 'VolumeId',
'Value': volume_id
}
],
StartTime=datetime.datetime.now() - datetime.timedelta(days=7),
EndTime=datetime.datetime.now(),
Period=86400,
Statistics=['Average']
)
# Recommend optimization based on usage
if not metrics['Datapoints']:
print(f"Volume {volume_id} appears unused - consider deletion")
elif volume_type == 'gp2' and size_gb > 100:
print(f"Volume {volume_id} - consider migrating to gp3 for better cost/performance")

Database Cost Optimization

1. RDS Optimization

RDS Instance Rightsizing

-- Monitor database performance
SELECT
database_name,
connection_count,
cpu_utilization,
memory_utilization,
storage_utilization
FROM aws_rds_performance_metrics
WHERE timestamp >= NOW() - INTERVAL '7 days'
ORDER BY cpu_utilization DESC;
-- Identify unused databases
SELECT
instance_identifier,
engine,
status,
last_connection_time
FROM aws_rds_instances
WHERE last_connection_time < NOW() - INTERVAL '30 days'
AND status = 'available';

RDS Reserved Instances

def purchase_rds_reserved_instance():
client = boto3.client('rds')
response = client.purchase_reserved_db_instances_offering(
ReservedDBInstancesOfferingId='rds-reserved-instance-offering-id',
DBInstanceCount=1,
ReservedDBInstanceId='my-reserved-instance',
Tags=[
{
'Key': 'Environment',
'Value': 'production'
}
]
)
return response

2. DynamoDB Optimization

DynamoDB Capacity Planning

import boto3
def optimize_dynamodb_capacity():
client = boto3.client('dynamodb')
tables = client.list_tables()['TableNames']
for table_name in tables:
# Get table metrics
cloudwatch = boto3.client('cloudwatch')
consumed_read = cloudwatch.get_metric_statistics(
Namespace='AWS/DynamoDB',
MetricName='ConsumedReadCapacityUnits',
Dimensions=[{'Name': 'TableName', 'Value': table_name}],
StartTime=datetime.datetime.now() - datetime.timedelta(days=7),
EndTime=datetime.datetime.now(),
Period=3600,
Statistics=['Sum']
)
consumed_write = cloudwatch.get_metric_statistics(
Namespace='AWS/DynamoDB',
MetricName='ConsumedWriteCapacityUnits',
Dimensions=[{'Name': 'TableName', 'Value': table_name}],
StartTime=datetime.datetime.now() - datetime.timedelta(days=7),
EndTime=datetime.datetime.now(),
Period=3600,
Statistics=['Sum']
)
# Calculate optimal capacity
avg_read = sum(d['Sum'] for d in consumed_read['Datapoints']) / len(consumed_read['Datapoints'])
avg_write = sum(d['Sum'] for d in consumed_write['Datapoints']) / len(consumed_write['Datapoints'])
print(f"Table {table_name}:")
print(f" Average Read: {avg_read:.2f} units/hour")
print(f" Average Write: {avg_write:.2f} units/hour")

Network Cost Optimization

1. Data Transfer Optimization

VPC Endpoint Strategy

VPC Endpoints:
Gateway Endpoints:
- S3: No data transfer costs for S3 access
- DynamoDB: No data transfer costs for DynamoDB access
Interface Endpoints:
- API Gateway: Reduces internet egress costs
- CloudWatch: No data transfer for monitoring
- Secrets Manager: Secure access without internet
Cost Savings:
- S3 Gateway: $0.01 per GB saved
- DynamoDB Gateway: $0.02 per GB saved
- Interface Endpoints: $0.01 per GB saved

CloudFront CDN Optimization

def configure_cloudfront_distribution():
client = boto3.client('cloudfront')
distribution_config = {
'CallerReference': 'my-distribution-2024',
'Comment': 'Optimized distribution for cost savings',
'DefaultRootObject': 'index.html',
'Origins': {
'Quantity': 1,
'Items': [
{
'Id': 'S3-my-bucket',
'DomainName': 'my-bucket.s3.amazonaws.com',
'S3OriginConfig': {
'OriginAccessIdentity': 'origin-access-identity/cloudfront/E1234567890ABCDEF'
}
}
]
},
'DefaultCacheBehavior': {
'TargetOriginId': 'S3-my-bucket',
'ViewerProtocolPolicy': 'redirect-to-https',
'MinTTL': 86400,
'Compress': True,
'ForwardedValues': {
'QueryString': False,
'Cookies': {'Forward': 'none'}
}
},
'PriceClass': 'PriceClass_100', # Use cheapest edge locations
'Enabled': True
}
return client.create_distribution(DistributionConfig=distribution_config)

Monitoring and Cost Management

1. AWS Cost Explorer

Cost Analysis Queries

import boto3
def analyze_costs():
client = boto3.client('ce')
# Get cost by service
response = client.get_cost_and_usage(
TimePeriod={
'Start': '2024-01-01',
'End': '2024-01-31'
},
Granularity='MONTHLY',
Metrics=['BlendedCost'],
GroupBy=[
{'Type': 'DIMENSION', 'Key': 'SERVICE'},
{'Type': 'DIMENSION', 'Key': 'REGION'}
]
)
for result in response['ResultsByTime']:
print(f"Period: {result['TimePeriod']['Start']} to {result['TimePeriod']['End']}")
for group in result['Groups']:
service = group['Keys'][0]
cost = group['Metrics']['BlendedCost']['Amount']
print(f" {service}: ${cost}")
# Get RI utilization
def get_ri_utilization():
client = boto3.client('ce')
response = client.get_reservation_utilization(
TimePeriod={
'Start': '2024-01-01',
'End': '2024-01-31'
},
GroupBy=[
{'Type': 'DIMENSION', 'Key': 'SUBSCRIPTION_ID'}
]
)
return response['UtilizationsByTime']

2. AWS Budgets

Budget Configuration

def create_cost_budget():
client = boto3.client('budgets')
budget = {
'BudgetName': 'Monthly-Cost-Budget',
'BudgetType': 'COST',
'TimeUnit': 'MONTHLY',
'BudgetLimit': {
'Amount': '10000',
'Unit': 'USD'
},
'CostFilters': {
'Service': ['Amazon EC2', 'Amazon RDS', 'Amazon S3']
},
'NotificationWithSubscribers': [
{
'Notification': {
'NotificationType': 'ACTUAL',
'ComparisonOperator': 'GREATER_THAN',
'Threshold': 80,
'ThresholdType': 'PERCENTAGE'
},
'Subscribers': [
{
'SubscriptionType': 'EMAIL',
'Address': 'admin@company.com'
}
]
}
]
}
return client.create_budget(
AccountId='123456789012',
Budget=budget
)

Automation and Governance

1. Cost Optimization Automation

Lambda Function for Instance Rightsizing

import boto3
import json
def lambda_handler(event, context):
ec2_client = boto3.client('ec2')
cloudwatch = boto3.client('cloudwatch')
# Get all running instances
instances = ec2_client.describe_instances(
Filters=[{'Name': 'instance-state-name', 'Values': ['running']}]
)
recommendations = []
for reservation in instances['Reservations']:
for instance in reservation['Instances']:
instance_id = instance['InstanceId']
instance_type = instance['InstanceType']
# Get CPU utilization metrics
cpu_metrics = cloudwatch.get_metric_statistics(
Namespace='AWS/EC2',
MetricName='CPUUtilization',
Dimensions=[{'Name': 'InstanceId', 'Value': instance_id}],
StartTime=datetime.datetime.now() - datetime.timedelta(days=7),
EndTime=datetime.datetime.now(),
Period=3600,
Statistics=['Average']
)
if cpu_metrics['Datapoints']:
avg_cpu = sum(d['Average'] for d in cpu_metrics['Datapoints']) / len(cpu_metrics['Datapoints'])
# Generate recommendation
if avg_cpu < 20:
recommendations.append({
'instance_id': instance_id,
'current_type': instance_type,
'recommended_action': 'Downsize or use Spot',
'avg_cpu': avg_cpu
})
elif avg_cpu > 80:
recommendations.append({
'instance_id': instance_id,
'current_type': instance_type,
'recommended_action': 'Upsize or add instances',
'avg_cpu': avg_cpu
})
return {
'statusCode': 200,
'body': json.dumps(recommendations)
}

2. Tagging Enforcement

Tagging Policy

def enforce_tagging():
client = boto3.client('ec2')
# Get all resources without required tags
resources = client.describe_tags(
Filters=[
{'Name': 'resource-type', 'Values': ['instance']},
{'Name': 'key', 'Values': ['Environment', 'Project', 'Owner']}
]
)
untagged_instances = set()
for tag in resources['Tags']:
if tag['Key'] not in ['Environment', 'Project', 'Owner']:
untagged_instances.add(tag['ResourceId'])
# Stop untagged instances
for instance_id in untagged_instances:
client.stop_instances(InstanceIds=[instance_id])
print(f"Stopped untagged instance: {instance_id}")

Best Practices and Strategies

1. Cost Optimization Framework

FINOPS Principles

Inform:
- Provide cost visibility
- Share cost data with teams
- Educate on cost drivers
Optimize:
- Implement cost optimization strategies
- Continuously monitor and adjust
- Automate where possible
Govern:
- Establish cost policies
- Implement cost controls
- Regular cost reviews

2. Cost Optimization Checklist

Monthly Review Items

Quarterly Optimization Tasks

Tools and Resources

AWS Native Tools

Third-Party Tools

Conclusion

AWS cost optimization is an ongoing process that requires continuous monitoring, analysis, and optimization. By implementing the strategies outlined in this guide—rightsizing, using Reserved Instances and Savings Plans, optimizing storage, leveraging Spot Instances, and implementing proper governance—you can achieve significant cost savings while maintaining performance and reliability.

Remember that cost optimization is not about cutting corners; it’s about using resources efficiently and eliminating waste. Start with the highest-impact optimizations, implement proper monitoring and automation, and establish a culture of cost consciousness across your organization.

The key to successful cost optimization is making it a continuous process rather than a one-time project. Regular reviews, automated monitoring, and team education will ensure that your AWS costs remain optimized as your infrastructure and requirements evolve.


Edit page
Share this post on:

Previous Post
React Performance Optimization: Complete Guide for 2025
Next Post
Docker Containerization: Complete Best Practices Guide