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.

Talk to an engineer

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.