Back to Blog
AWSEBSstoragereduce_costs

EBS gp3 vs gp2, and why you should have migrated already

gp3 decouples IOPS and throughput from capacity and costs ~20% less than gp2, plus the snapshot and delete-on-termination mechanics that quietly inflate EBS bills.

JohannaJanuary 31, 20265 min read

gp3 has been generally available since late 2020. If you are still running gp2 general-purpose volumes in 2026, you are paying roughly 20% more for storage and, in many cases, paying a second time by over-provisioning capacity you don't need just to buy IOPS you do. The migration is an online, no-downtime volume modification. There is almost no reason left not to have done it, and a few good reasons the stragglers persist — mostly a misunderstanding of how gp2 ties performance to size. Let's take the whole mechanism apart.

Prices are us-east-1 and will vary by region and over time; the structural difference between the two volume types is the point.

gp2 chains IOPS to capacity

A gp2 volume's baseline performance is a function of its size: 3 IOPS per GB. A 100GB gp2 volume gets 300 baseline IOPS. To get 3,000 IOPS you need a 1,000GB volume, whether or not you need 1,000GB of space. Small volumes (under 1TB) can burst to 3,000 IOPS by draining a burst-credit bucket, but the bucket refills at the baseline rate — lean on it continuously and you fall back to your 3-IOPS-per-GB floor at exactly the moment your workload is busiest. Throughput on gp2 is likewise coupled, scaling with volume size up to 250 MB/s.

The result is a familiar anti-pattern: a database that needs 5,000 sustained IOPS but only 200GB of data, running on a 1,700GB gp2 volume because that's the size required to guarantee the IOPS. You pay for 1,500GB of storage you will never fill. gp2 is $0.10/GB-month, so that padding is ~$150/month per volume, forever.

gp3 decouples them, and starts cheaper

gp3 breaks the link. Every gp3 volume includes a baseline 3,000 IOPS and 125 MB/s throughput for free, at any size, and storage is ~$0.08/GB-month — about 20% cheaper than gp2 out of the gate. Need more than the baseline? You provision it independently:

  • Additional IOPS above 3,000: ~$0.005 per provisioned IOPS-month, up to 16,000 IOPS.
  • Additional throughput above 125 MB/s: ~$0.040 per MB/s-month, up to 1,000 MB/s.

Take that 200GB / 5,000-IOPS database. On gp3 it's a 200GB volume ($16/month) with the free 3,000 IOPS plus 2,000 provisioned IOPS ($10/month) — about $26/month, versus ~$170/month for the 1,700GB gp2 volume that existed only to manufacture IOPS. Even a plain like-for-like volume with no extra IOPS saves the flat 20%: a 500GB gp2 volume is $50/month, the same gp3 volume is $40/month, and the gp3 version also gives you 3,000 IOPS instead of 1,500.

The migration is a live modify

You do not snapshot-and-restore. ModifyVolume (console, CLI, or IaC) changes a gp2 volume to gp3 in place, online, with no detach and no downtime — the volume stays attached and the instance keeps running.

aws ec2 modify-volume --volume-id vol-0abc123 --volume-type gp3

Two things to know. First, there is a cooldown of roughly six hours after a modification before you can modify the same volume again, so batch your changes deliberately. Second, if the volume was relying on gp2 burst behavior for peak IOPS, set gp3's provisioned IOPS/throughput to match your real peak — the free 3,000/125 baseline covers most general-purpose workloads, but a busy volume that used to burst to 3,000 wants those numbers pinned explicitly rather than assumed.

When io2 Block Express is worth the premium

gp3 tops out at 16,000 IOPS, 1,000 MB/s, and does not offer a durability SLA beyond the standard EBS annual failure rate. io2 Block Express exists for the workloads that exceed those limits: up to 256,000 IOPS and 4,000 MB/s per volume, sub-millisecond and, more importantly, consistent latency, and a 99.999% durability guarantee. Storage runs ~$0.125/GB-month plus tiered IOPS pricing (the first band around $0.065 per provisioned IOPS-month, dropping in higher bands).

io2 earns its premium for tier-1 transactional databases where a latency spike is an incident and where you need tens of thousands of IOPS with a hard durability commitment. For everything else — application servers, most databases, log volumes, general workloads — gp3 is the correct default and io2 is money spent on headroom you won't use.

Snapshots: incremental, but not free of surprises

EBS snapshots are incremental at the block level. The first snapshot copies every used block; each later snapshot stores only blocks that changed since the previous one, at ~$0.05/GB-month of stored blocks. That sounds cheap, and per-snapshot it is. The bill comes from two directions.

The first is deletion semantics. Because snapshots share blocks, deleting a snapshot only frees the blocks no other snapshot still references. A chain of 200 daily snapshots doesn't shrink linearly when you prune the middle — you may delete a snapshot and reclaim almost nothing, because its blocks are still referenced elsewhere. People assume "delete old snapshots" is a reliable lever; often it barely moves the number.

The second is orphaned-snapshot sprawl. Snapshots outlive the volumes and instances that made them. Terminate an instance and its snapshots remain, unattached to anything, billing quietly for years. A months-old account almost always has snapshots of volumes that no longer exist, from AMIs long deregistered. Audit by owner and creation date, and consider EBS Snapshot Archive (~$0.0125/GB-month) for snapshots you must retain for compliance but will rarely restore — noting its 90-day minimum and a per-GB retrieval fee to pull them back to the standard tier.

Two footguns worth naming

Delete-on-termination. Root volumes default to DeleteOnTermination = true; additional volumes attached later default to false. Two symmetric failures follow. Data-bearing volumes left at the default false survive instance termination and become orphaned volumes billing at full storage rates with nothing attached — the volume-level twin of orphaned snapshots. And a data volume someone flipped to true for convenience gets destroyed the instant the instance is terminated, which is a fine way to lose a database during a routine scale-in. Set this flag deliberately per volume; never inherit the default and hope.

Unattached volumes generally. A gp3 volume in the available state — created, never attached, or detached and forgotten — costs the same per GB as one in active use. available-state volumes and unassociated snapshots are the two cheapest wins in most EBS audits precisely because nobody is looking at them.

The short version

Migrate gp2 to gp3 for the flat ~20% storage discount and stop over-sizing volumes to buy IOPS — provision IOPS and throughput directly instead. Reserve io2 Block Express for genuinely high-IOPS, latency-sensitive, durability-critical databases. Then go find the orphaned snapshots and available volumes, because those are pure waste with no workload behind them at all. None of this requires downtime, and most of it is a modify command and an afternoon of cleanup.