How Does AWS EBS Pricing Actually Work?

EBS bills provisioned capacity, provisioned performance and snapshots as three separate meters — and none of them stop when the instance does.

Published August 14, 2026 · Last updated August 14, 2026

Nobody sets out to buy EBS. You launch an instance, accept the default root volume, add a data disk because the database needs one, and the storage line on your bill assembles itself out of decisions that were each about something else. Then one month the block-storage number is a few hundred dollars, nobody can say which volumes produced it, and there is no console page that totals it for you.

The reason it is hard to reason about is that EBS is not one price. It is three meters that behave differently, and the one people assume is dominant usually is not.

What am I actually paying for on an EBS volume?

Capacity you provisioned, not capacity you used. This is the single most important thing about EBS billing. A 500 GB volume with 40 GB of data on it bills as 500 GB. AWS has no visibility into your file system and no discount for empty blocks — you are renting the disk, not the bytes.

Performance, separately, on some volume types. gp3 and io2 price IOPS and throughput as their own line items, independent of size. This is what makes gp3 cheap and io2 expensive, and it is the part that surprises people reading a bill for the first time.

Snapshots, forever. Snapshot storage is its own meter with its own rate, and nothing expires it unless you built something that does.

Two mechanics sit underneath all three. Charges accrue in per-second increments with a 60-second minimum, so a volume created for a ten-minute experiment costs ten minutes — but a volume left behind after that experiment costs every second until someone deletes it. And the state of the volume is irrelevant to the rate. Attached, detached, attached to an instance that has been stopped since March: all bill identically. Stopping an EC2 instance stops the compute charge and nothing else.

What does each EBS volume type cost per GB-month?

These are the rates AWS publishes in its own pricing examples and documentation:

What you are paying forRate
General Purpose SSD (gp3)$0.08 per GB-month
General Purpose SSD (gp2)$0.10 per GB-month
Provisioned IOPS SSD (io1 / io2)$0.125 per GB-month
Throughput Optimized HDD (st1)$0.045 per GB-month
Cold HDD (sc1)$0.015 per GB-month
Snapshot storage (standard tier)$0.05 per GB-month
Snapshot storage (archive tier)$0.0125 per GB-month

The spread between the top and bottom rows is more than 8×, which is the first place a bill leaks: volume type is usually chosen once, at launch, by whatever the console offered, and never revisited against what the workload turned out to need.

On io2 the storage row is not the interesting one. Provisioned IOPS are billed at $0.065 per provisioned IOPS-month for the first 32,000, so a 500 GB io2 volume provisioned at 20,000 IOPS costs $62.50 in storage and $1,300 in IOPS. If a database volume was provisioned for a load test two years ago and never turned back down, that is where the money is — not in the gigabytes.

Why is gp3 cheaper than gp2 even at the same performance?

Because the two types bundle performance completely differently.

On gp2, performance is welded to size. You get 3 IOPS per GiB — floored at 100, capped at 16,000, which you only reach at 5,334 GiB — and there is no way to buy more without buying capacity you do not need. Volumes under 1 TiB can burst to 3,000 IOPS on I/O credits, with an accrual limit of 5.4 million credits, enough to sustain that burst for about 30 minutes before you are back at baseline.

On gp3, performance is unbundled and burst does not exist. Every volume includes 3,000 IOPS and 125 MiB/s whatever its size, sustained indefinitely, and anything beyond that is bought explicitly at $0.005 per provisioned IOPS-month above 3,000 and $0.04 per provisioned MiB/s-month above 125.

Work that through and the migration arithmetic is not close. AWS's own examples put a 100 GiB gp2 volume at $10.00 a month; the same volume on gp3 at baseline is $8.00, and even configured to match gp2's 128 MiB/s exactly it is $8.12 — 19% less for identical performance. At 500 GiB it is $50.00 against $45.00 matched; at 1,000 GiB, $100.00 against $85.00.

The small-volume case is better than the discount suggests. That 100 GiB gp2 volume has a baseline of just 300 IOPS and leans on burst credits to reach 3,000. On gp3 it gets a flat 3,000 IOPS with no credit balance to run out of at the worst possible moment. You are paying 20% less for a volume that is genuinely faster under sustained load.

How do I see what my own volumes are costing?

Start with an inventory. This is the whole picture in one call — type, size, state, and the provisioned performance that is billing alongside it:

aws ec2 describe-volumes \
  --query 'sort_by(Volumes,&Size)[].[VolumeId,VolumeType,Size,State,Iops,Throughput]' \
  --output table \
  --region us-east-1

Two things to read in that output. Any row in the available state is a volume attached to nothing, billing at the full rate. Any gp2 row is paying the older price for the older performance model.

For the totals, sum by type — the number that turns "we have some gp2 left" into a monthly figure:

# Total gp2 GB in this region — multiply by $0.10, then by 0.80 for the gp3 equivalent
aws ec2 describe-volumes \
  --filters Name=volume-type,Values=gp2 \
  --query 'sum(Volumes[].Size)' \
  --region us-east-1

Run both against every region you have ever launched anything in, not just your default. EBS is regional, and the volumes nobody remembers are disproportionately in the regions nobody looks at. In Cost Explorer, the equivalent view is the EC2 service filtered and grouped by usage type, which splits the storage, provisioned-IOPS and snapshot meters apart so you can see which of the three is actually growing.

How do I bring the bill down by hand?

Migrate gp2 volumes to gp3. This is the least disruptive change on the list. Elastic Volumes modifies the type of a live volume without detaching it or stopping the instance, and the volume stays readable and writable while AWS works:

aws ec2 modify-volume --volume-id vol-0123456789abcdef0 --volume-type gp3

Do not blindly reproduce gp2's throughput ceiling when you do. A volume above 170 GiB has a 250 MiB/s ceiling on gp2, and paying gp3 to match it costs $5.00 a month for throughput that most workloads never come close to using. Check what the volume actually moves before provisioning anything above the included 125 MiB/s.

Turn down performance nobody uses. The same call adjusts --iops and --throughput. On io2 at $0.065 per provisioned IOPS-month, walking a volume back from 20,000 to 5,000 IOPS saves $975 a month from one command.

Then deal with the snapshots, which for most accounts is the meter growing fastest. The archive tier is $0.0125 per GB-month against the standard $0.05 — 75% off — and it is aimed exactly at the snapshot taken before a migration in 2023 that nobody will ever restore but nobody will delete either.

What else bills the same way?

EBS is one instance of a pattern that repeats across every storage service on your bill: capacity provisioned once, at a hot rate, for data that went cold years ago. The same shape sits in S3, where objects stay in Standard indefinitely unless a rule moves them and unfinished multipart uploads bill invisibly — lifecycle rules for cold object data covers the rule that clears both. It sits in EFS too, at a much steeper multiple: why an EFS bill runs so far ahead of its EBS equivalent works through the storage classes and the access-time trap that stops lifecycle policies from moving anything at all.

If EBS is simply the first line item you have looked at closely, it is unlikely to be the largest. Which parts of an AWS bill are worth your afternoon puts the whole account in order of savings per minute, and deleting things nobody uses generally beats re-pricing things people do.

How do I stop paying for storage nobody chose?

Pricing an EBS estate once is an afternoon. Keeping it priced correctly is the part that never happens twice, because the inputs move: every new instance adds a volume, every terminated instance may leave one behind, every gp2 volume created from an old launch template or an AMI default starts paying the older rate again, and every region has its own copy of the problem. That is what a scan is for. Parsivex checks each region, flags gp2 volumes where the like-for-like gp3 configuration is strictly cheaper and volumes billing while attached to nothing, and then keeps watching — so the storage created after your cleanup gets caught in weeks rather than whenever somebody next reads the bill closely.

For what those findings mean once they appear in your report, including the volume sizes where migrating is and is not worth it, see Unmigrated gp2 volume and Orphaned EBS volume, or read how scans work before you connect an account.