Why
gp3 is cheaper per GB than gp2 and gives a baseline of IOPS and throughput regardless of size, with performance you can raise independently. On gp2, IOPS scale with size, which is why people over-provision big volumes just for IOPS.
1. Find gp2 volumes
for r in $(aws ec2 describe-regions --query "Regions[].RegionName" --output text); do
aws ec2 describe-volumes --region $r --filters Name=volume-type,Values=gp2 \
--query "Volumes[].[VolumeId,Size,State,Attachments[0].InstanceId]" --output text | sed "s/^/$r /"
done > gp2-volumes.txt
wc -l gp2-volumes.txt
2. Match performance
gp2 performance: 3 IOPS per GiB (minimum 100, up to the gp2 maximum) plus burst to 3,000 IOPS for smaller volumes. gp3 baseline: 3,000 IOPS and 125 MiB/s.
| gp2 size | gp2 baseline IOPS | gp3 setting |
|---|---|---|
| Up to 1,000 GiB | Up to 3,000 (burst to 3,000) | Baseline 3,000 is enough |
| Larger | 3 × size | Set --iops to 3 × size (or measured peak) |
| Throughput-heavy (big sequential reads) | Up to 250 MiB/s | Set --throughput (e.g. 250) |
Check real usage before deciding: CloudWatch VolumeReadOps, VolumeWriteOps, VolumeReadBytes, VolumeWriteBytes over a busy week. Check the current gp2/gp3 limits in the EBS documentation.
3. Convert (online)
aws ec2 modify-volume --volume-id vol-0abc123 --volume-type gp3 --iops 3000 --throughput 125
aws ec2 describe-volumes-modifications --volume-ids vol-0abc123 \
--query "VolumesModifications[0].{state:ModificationState, progress:Progress}"
The volume stays attached and usable during modifying and optimizing. Performance during optimisation is between the old and new specification.
Batch conversion for small volumes:
awk '$3 <= 1000 {print $1, $2}' gp2-volumes.txt | while read region vol; do
aws ec2 modify-volume --region $region --volume-id $vol --volume-type gp3
done
Note
After a modification you must wait (currently six hours) before modifying the same volume again. Get the settings right the first time for large volumes.
4. Stop creating gp2
- Launch templates and AMI block device mappings: set
VolumeType: gp3. - IaC defaults: Terraform
aws_ebs_volume.type = "gp3", CloudFormationVolumeType: gp3. - EKS: a
gp3StorageClass (EBS CSI driver) as default; the oldgp2class as non-default.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3
annotations: { storageclass.kubernetes.io/is-default-class: "true" }
provisioner: ebs.csi.aws.com
parameters: { type: gp3 }
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
- AWS Config custom rule or a periodic report for any remaining gp2 volumes.
Checklist
- gp2 inventory across all regions and accounts
- IOPS/throughput set from measured usage for large volumes
- All volumes converted; modifications completed
- Launch templates, AMIs, IaC and StorageClasses default to gp3