In short:
- gp3 costs $0.08 per GB-month against $0.10 for gp2, and includes 3,000 IOPS and 125 MiB/s at any size.
- You can switch a volume from gp2 to gp3 while it stays attached and in use.
- gp2 volumes of 334 GiB and up lose throughput, and those over 1 TiB lose IOPS, unless you set both on gp3.
gp2 vs gp3 has a short answer: gp3 is the better EBS volume for almost every workload, and it costs 20% less. gp3 storage is $0.08 per GB-month against $0.10 for gp2, and every gp3 volume gets 3,000 IOPS and 125 MiB/s included, whatever its size. You can switch a live volume in place, without stopping the instance.
Most AWS accounts I review still have gp2 volumes. They were created by old launch templates, old Terraform modules, or an EKS cluster's default StorageClass, and nobody went back. This post shows how to find them, switch them safely, and stop new ones from appearing.
gp2 vs gp3 at a glance
| Feature | gp2 | gp3 |
|---|---|---|
| Price (us-east-1) | $0.10 per GB-month | $0.08 per GB-month |
| Baseline IOPS | 3 per GiB (100 minimum) | 3,000 at any size |
| Burst | Up to 3,000 IOPS on volumes under 1 TiB, while credits last | No burst needed, performance is constant |
| Maximum IOPS | 16,000 | 80,000 |
| Throughput | 128 to 250 MiB/s, depending on size | 125 MiB/s included, up to 2,000 MiB/s |
| Extra performance | Only by making the volume bigger | Paid separately: $0.005 per IOPS, $0.04 per MiB/s |
The important difference is the last row. On gp2, IOPS are tied to size: a 100 GiB gp2 volume gets 300 baseline IOPS, and the only way to get more is to buy more disk. On gp3, size and performance are separate, so you pay for the storage you need and add performance only if you need it.
gp3 limits went up in September 2025: a gp3 volume can now be up to 64 TiB, with 80,000 IOPS and 2,000 MiB/s (AWS announcement).
What gp2 to gp3 saves: real numbers
Here is what the switch does for typical volumes in us-east-1:
| Volume | gp2 per month | gp3 per month | Saving |
|---|---|---|---|
| 10 root volumes of 30 GiB | $30 | $24 | $6 (20%), and 3,000 IOPS each instead of 100 |
| 500 GiB database volume | $50 | $40 (+$5 to keep 250 MiB/s) | $5 to $10, and 3,000 IOPS instead of 1,500 |
| 2,000 GiB volume, matching gp2 performance | $200 | $180 | $20 (10%) |
The 2,000 GiB row is the one to watch. A gp2 volume that size has 6,000 IOPS and 250 MiB/s. To match it on gp3 you pay for 3,000 extra IOPS ($15) and 125 extra MiB/s ($5) on top of $160 of storage. It is still cheaper, but only if you set those numbers. That is the mistake in the next section.
Storage is rarely the biggest line on an AWS bill, but this is the safest saving there is: no code change, no restart, no risk. For the bigger wins, see how to reduce your AWS bill.
The big-volume IOPS trap
When you change a volume to gp3 without saying anything else, AWS gives it the gp3 baseline: 3,000 IOPS and 125 MiB/s.
For IOPS that is more than gp2 gives any volume under 1 TiB. For throughput, and for very big volumes, it can be less:
- A 2,000 GiB gp2 volume runs at 6,000 IOPS. Switched with defaults, it drops to 3,000.
- Any gp2 volume of 334 GiB or more delivers 250 MiB/s. Switched with defaults, it drops to 125 MiB/s.
Before you switch a large volume, check its baseline (size × 3, up to 16,000) and look at its real usage in CloudWatch (VolumeReadOps, VolumeWriteOps, VolumeReadBytes, VolumeWriteBytes). Then set --iops and --throughput to what it actually uses, not what gp2 happened to give it. Many big volumes use far less than their gp2 baseline, which makes the saving bigger.
Step 1: find every gp2 volume
Volumes live in regions, so check them all. This lists every gp2 volume in the account with its region, size and state:
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]' --output text |
sed "s/^/$r /"
done
Volumes in the available state are not attached to anything. Do not convert those: snapshot them and delete them instead, as explained in 3 hidden AWS charges.
Step 2: switch a volume from gp2 to gp3, live
For a volume under 334 GiB, one command is enough:
aws ec2 modify-volume --volume-id vol-0abc123def4567890 --volume-type gp3
For a volume of 334 GiB or more, set the performance it needs (here, matching a 2,000 GiB gp2 volume):
aws ec2 modify-volume --volume-id vol-0abc123def4567890 \
--volume-type gp3 --iops 6000 --throughput 250
Then watch the progress:
aws ec2 describe-volumes-modifications --volume-ids vol-0abc123def4567890 \
--query 'VolumesModifications[].[ModificationState,Progress]' --output text
The volume stays attached and keeps serving reads and writes while it changes. Small volumes finish in minutes; large ones can take hours. You can modify a volume up to four times in a rolling 24-hour window, each change after the previous one completes (ModifyVolume reference).
Two habits make this boring:
- Take a snapshot first if the volume holds anything you cannot rebuild. The change is safe, but a snapshot costs little and lets you sleep.
- Switch one non-production volume, watch it for a day, then do the rest.
Step 3: switch gp2 to gp3 in Terraform
If Terraform created the volume, change it in Terraform, or the next terraform apply will switch it back.
A standalone volume:
resource "aws_ebs_volume" "data" {
availability_zone = "us-east-1a"
size = 500
type = "gp3"
# Only needed above the gp3 baseline of 3,000 IOPS and 125 MiB/s:
# iops = 6000
# throughput = 250
}
An instance's root volume:
resource "aws_instance" "app" {
ami = "ami-0123456789abcdef0"
instance_type = "t3.medium"
root_block_device {
volume_type = "gp3"
volume_size = 30
}
}
A launch template, so every new instance starts on gp3:
resource "aws_launch_template" "app" {
name_prefix = "app-"
block_device_mappings {
device_name = "/dev/xvda"
ebs {
volume_type = "gp3"
volume_size = 30
}
}
}
Run terraform plan and read it before you apply. A volume type change should show as update in-place. If the plan says a resource will be replaced, stop and find out why first: replacing an instance or a volume is not the same as modifying it.
Step 4: EKS and Kubernetes volumes
Older EKS clusters ship with a gp2 StorageClass as the default, so every new PersistentVolume is gp2. With the EBS CSI driver installed, add a gp3 StorageClass and make it the 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
Then remove the default annotation from the old gp2 class, so only one class is the default. Existing PersistentVolumes keep their EBS volume, so switch those with the same aws ec2 modify-volume command from Step 2; the pods keep running. For more on keeping pods healthy, see 3 Kubernetes mistakes that crash your pods.
Stop new gp2 volumes from appearing
Switching today's volumes is half the job. The other half is making sure next month's volumes are gp3:
-
Default EBS volume type in Terraform modules and launch templates: set
gp3everywhere a volume type appears. - EKS default StorageClass: gp3, as above.
- A monthly check: run the Step 1 loop, or add it to the routine in the AWS cost checklist.
gp2 vs gp3 checklist
- List every gp2 volume in every region.
- Delete the unattached ones instead of converting them.
- Switch volumes under 334 GiB with one command.
- For volumes of 334 GiB and up, check real IOPS and throughput in CloudWatch, and set them on gp3.
- Change the type in Terraform too, and check the plan says update in-place.
- Make gp3 the default in launch templates, modules and the EKS StorageClass.
Want a rough number for your whole account first? The AWS cost savings calculator has gp2 as one of its checks. If you would rather have someone go through the account with you, the AWS cost optimization review covers this and the bigger wins, or start with a Free AWS Review.
Prices and limits are from the Amazon EBS General Purpose volumes page and the EBS user guide, checked in November 2026. Prices vary slightly by region.
Common questions
Is gp3 always cheaper than gp2?
For almost every volume, yes. gp3 storage is 20% cheaper, and below 1 TiB you get more IOPS for free. Very large gp2 volumes need extra IOPS and throughput bought on gp3 to match, which narrows the saving, but it usually stays cheaper.
Do I need to stop my EC2 instance to change gp2 to gp3?
No. On current-generation instance types you change the volume type while it stays attached and in use. The volume keeps serving reads and writes during the change.
How long does a gp2 to gp3 change take?
It depends on the volume size and how busy it is: small volumes finish in minutes, large ones can take hours. You can keep using the volume the whole time.
Can I go back from gp3 to gp2?
Yes, with the same modify-volume command. You can modify a volume up to four times in a rolling 24-hour window, once the previous change has completed.
What about Kubernetes volumes on EKS?
Set a gp3 StorageClass as the default for new volumes, then switch existing volumes with the same AWS CLI command. The pods keep running.
Originally published on DevOps with Umar. I keep free browser tools for this kind of work there: an AWS savings estimator, a cron expression generator and more in the tools list.
Top comments (0)