EBS Volume Optimization Strategies
Optimize EBS volumes for cost and performance by selecting appropriate types, sizing, and implementing snapshot management.
EBS volume optimization balances performance requirements with cost efficiency through appropriate type selection, right-sizing, and snapshot management.
Volume Type Selection
Performance Comparison
| Type | IOPS | Throughput | Use Case | Cost |
|---|---|---|---|---|
| gp3 | 3,000-16,000 | 125-1,000 MB/s | General purpose | $0.08/GB |
| gp2 | 100-16,000 (burst) | 250 MB/s | Legacy general | $0.10/GB |
| io2 | Up to 256,000 | 4,000 MB/s | High IOPS | $0.125/GB |
| st1 | 500 | 500 MB/s | Throughput | $0.045/GB |
| sc1 | 250 | 250 MB/s | Cold storage | $0.025/GB |
Migration to gp3
Automated Migration Script
import boto3
ec2 = boto3.client('ec2')
def migrate_gp2_to_gp3():
volumes = ec2.describe_volumes(
Filters=[
{'Name': 'volume-type', 'Values': ['gp2']}
]
)
migration_results = []
for volume in volumes['Volumes']:
volume_id = volume['VolumeId']
current_size = volume['Size']
# gp2 baseline IOPS = 3 * size (min 100, max 16,000)
gp2_iops = min(max(current_size * 3, 100), 16000)
# gp3 default is 3,000 IOPS, 125 MB/s throughput
# Only provision more if gp2 had more
gp3_iops = max(3000, gp2_iops) if gp2_iops > 3000 else 3000
gp3_throughput = 125 # Default, increase if needed
try:
ec2.modify_volume(
VolumeId=volume_id,
VolumeType='gp3',
Iops=gp3_iops,
Throughput=gp3_throughput
)
migration_results.append({
'volume_id': volume_id,
'status': 'success',
'old_type': 'gp2',
'new_type': 'gp3',
'iops': gp3_iops
})
except Exception as e:
migration_results.append({
'volume_id': volume_id,
'status': 'failed',
'error': str(e)
})
return migration_results
Calculate Savings
def calculate_gp3_savings(volume_id):
volume = ec2.describe_volumes(VolumeIds=[volume_id])['Volumes'][0]
size_gb = volume['Size']
# gp2 cost
gp2_cost = size_gb * 0.10
# gp3 cost (base + IOPS + throughput)
gp3_base = size_gb * 0.08
gp3_iops = max(0, (volume.get('Iops', 3000) - 3000)) * 0.005
gp3_throughput = max(0, (volume.get('Throughput', 125) - 125)) * 0.04
gp3_total = gp3_base + gp3_iops + gp3_throughput
return {
'gp2_monthly_cost': gp2_cost,
'gp3_monthly_cost': gp3_total,
'monthly_savings': gp2_cost - gp3_total,
'savings_percentage': ((gp2_cost - gp3_total) / gp2_cost) * 100
}
Right-Sizing Volumes
Utilization Analysis
def analyze_volume_utilization(volume_id, days=14):
cloudwatch = boto3.client('cloudwatch')
end_time = datetime.utcnow()
start_time = end_time - timedelta(days=days)
metrics = {}
for metric_name in ['VolumeReadOps', 'VolumeWriteOps',
'VolumeReadBytes', 'VolumeWriteBytes']:
response = cloudwatch.get_metric_statistics(
Namespace='AWS/EBS',
MetricName=metric_name,
Dimensions=[
{'Name': 'VolumeId', 'Value': volume_id}
],
StartTime=start_time,
EndTime=end_time,
Period=3600,
Statistics=['Average', 'Maximum']
)
if response['Datapoints']:
metrics[metric_name] = {
'average': sum(d['Average'] for d in response['Datapoints']) / len(response['Datapoints']),
'maximum': max(d['Maximum'] for d in response['Datapoints'])
}
return metrics
def recommend_volume_type(utilization, current_volume):
read_ops = utilization.get('VolumeReadOps', {}).get('average', 0)
write_ops = utilization.get('VolumeWriteOps', {}).get('average', 0)
total_iops = read_ops + write_ops
read_bytes = utilization.get('VolumeReadBytes', {}).get('average', 0)
write_bytes = utilization.get('VolumeWriteBytes', {}).get('average', 0)
throughput_mbps = (read_bytes + write_bytes) / (1024 * 1024)
if total_iops > 16000:
return 'io2'
elif throughput_mbps > 250 and total_iops < 500:
return 'st1'
elif total_iops < 100 and throughput_mbps < 40:
return 'sc1'
else:
return 'gp3'
Snapshot Management
Lifecycle Policy
def create_snapshot_lifecycle_policy():
dlm = boto3.client('dlm')
response = dlm.create_lifecycle_policy(
ExecutionRoleArn='arn:aws:iam::123456789012:role/DLMRole',
Description='Daily EBS snapshots with 7-day retention',
State='ENABLED',
PolicyDetails={
'PolicyType': 'EBS_SNAPSHOT_MANAGEMENT',
'ResourceTypes': ['VOLUME'],
'TargetTags': [
{'Key': 'Backup', 'Value': 'true'}
],
'Schedules': [
{
'Name': 'DailySnapshots',
'CreateRule': {
'Interval': 24,
'IntervalUnit': 'HOURS',
'Times': ['03:00']
},
'RetainRule': {
'Count': 7
},
'CopyTags': True
}
]
}
)
return response
def cleanup_orphaned_snapshots():
snapshots = ec2.describe_snapshots(
OwnerIds=['self']
)['Snapshots']
volumes = {v['VolumeId'] for v in ec2.describe_volumes()['Volumes']}
orphaned = []
for snap in snapshots:
if snap['VolumeId'] not in volumes:
orphaned.append(snap)
return orphaned
Working with Warqline
We are a cloud engineering consultancy and an official AWS and Google Cloud partner. If you are running this in production and want a second pair of eyes, we scope work in a free 45-minute technical call: you describe what you are running and what worries you, and we tell you what we would look at first.
Conclusion
EBS optimization combines volume type selection, right-sizing based on utilization, and efficient snapshot management. Migrate to gp3 for cost savings and implement DLM policies for automated snapshot lifecycle management.