Provisioned IOPS for databases: when paying for I/O is worth it
io2 Block Express charges $0.065 per provisioned IOPS per month on top of storage. Provision 40,000 IOPS and you have added $2,600 to the monthly bill before a single byte is stored. Here is when that is the right call.
Quick answer
On RDS, gp3 storage costs $0.115 per GB-month and includes 3,000 IOPS and 125 MB/s at no extra charge, with additional IOPS at $0.02 per IOPS-month and throughput at $0.08 per MB/s-month. io1 costs $0.125 per GB-month plus $0.10 per provisioned IOPS-month, and io2 Block Express costs $0.125 per GB-month plus tiered IOPS starting at $0.065. A 1 TB database needing 20,000 IOPS costs about $455 per month on gp3 ($115 storage plus $340 for 17,000 extra IOPS) versus about $1,425 on io1 ($125 plus $1,300). gp3 wins below its 16,000 IOPS ceiling; above that, io2 is the only option that scales.
Provisioned IOPS is the storage decision most likely to double a database bill without anyone noticing, because it is a per-unit charge on a number that looks like a configuration parameter rather than a purchase. Here is what each option costs and how to decide which you actually need.
The pricing, side by side
| Type | Storage | IOPS charge | Max IOPS (RDS) |
|---|---|---|---|
| gp3 | $0.115/GB-mo | 3,000 free, then $0.02/IOPS-mo | 16,000 |
| gp3 throughput | included 125 MB/s | $0.08 per MB/s-mo beyond | 1,000 MB/s |
| io1 | $0.125/GB-mo | $0.10/IOPS-mo | 64,000 |
| io2 Block Express | $0.125/GB-mo | $0.065/IOPS-mo to 32,000, then tiered lower | 256,000 |
| Aurora | $0.10/GB-mo | $0.20 per million I/O requests | n/a, pay per use |
The gap between gp3's $0.02 and io1's $0.10 per IOPS-month is a factor of five, and it is the single most expensive misconfiguration available in database storage. A team migrating an old io1 volume forward without revisiting the IOPS figure can pay five times the necessary rate for years.
Worked comparisons
| Requirement | gp3 | io1 | io2 |
|---|---|---|---|
| 500 GB, 3,000 IOPS | $57.50 | $362.50 | $257.50 |
| 1 TB, 12,000 IOPS | $295 | $1,325 | $905 |
| 1 TB, 20,000 IOPS | not available | $2,125 | $1,425 |
| 2 TB, 50,000 IOPS | not available | $5,250 | about $3,300 |
Two conclusions jump out. First, io1 has essentially no remaining use case: io2 delivers better durability and higher limits at a lower IOPS rate. If you still run io1 volumes, migrating to io2 is a pure saving, roughly 35 percent off the IOPS line, with no downside. Second, gp3 handles a very large share of real database workloads at a fraction of the price, and its 16,000 IOPS ceiling on RDS is higher than most OLTP databases actually need.
How many IOPS do you actually need
Measure, do not guess. On RDS, look at ReadIOPS plus WriteIOPS in CloudWatch at one-minute granularity over a representative fortnight. Take the p99 rather than the maximum, because a single backup window spike is not a provisioning requirement. Then add 30 percent headroom for growth and for the maintenance operations that burst I/O, such as index builds and vacuum.
The number that surprises people is how low it usually is. A well-indexed OLTP database with a working set that fits in the buffer pool does most reads from memory and generates far less disk I/O than its query rate suggests. A database serving 4,000 queries per second frequently sits at 1,500 to 3,000 disk IOPS, well inside gp3's free tier. If you are seeing 20,000 IOPS on an OLTP workload, the first question is whether an index is missing or the instance has too little memory, because buying I/O to compensate for a bad plan is the most expensive fix available.
Memory is usually the cheaper lever
Consider a database at 18,000 read IOPS because its working set exceeds the buffer pool. Option one: buy 18,000 provisioned IOPS on io2, about $1,170 per month. Option two: move from db.r6g.xlarge (32 GB, $378 per month) to db.r6g.2xlarge (64 GB, $756 per month), an increase of $378, and let the larger buffer pool absorb the reads. The memory route costs less than a third as much and improves latency more, because a page served from RAM beats a page served from NVMe by orders of magnitude.
This is the general rule: on read-heavy workloads, buy memory before you buy IOPS. Provisioned IOPS is genuinely necessary for write-heavy workloads where the write rate exceeds what gp3 can absorb, for latency-sensitive workloads that need consistent single-digit millisecond I/O, and for very large datasets where no realistic amount of memory holds the working set.
Throughput is the other half
IOPS is not the only limit. Analytical queries doing large sequential scans are throughput-bound, not IOPS-bound. gp3 includes 125 MB/s and sells more at $0.08 per MB/s-month, so raising it to 500 MB/s costs an extra $30 per month, which is inexpensive relative to the IOPS meter. If your slow queries are big scans, check throughput saturation before you touch IOPS at all.
Price storage type, IOPS, and throughput from Terraform before you merge, because these are three independent meters and only one of them is usually the constraint. Compare storage options against the resource catalog, and see throughput against IOPS pricing for the block-storage detail.
FAQ
How much do provisioned IOPS cost on RDS?
gp3 includes 3,000 IOPS with storage at $0.115 per GB-month and sells more at $0.02 per IOPS-month up to 16,000. io1 costs $0.125 per GB-month plus $0.10 per provisioned IOPS-month. io2 Block Express costs $0.125 per GB-month plus tiered IOPS starting at $0.065 and falling above 32,000. Aurora instead bills $0.20 per million I/O requests with no provisioning.
Is io1 ever the right choice now?
Essentially never. io2 Block Express delivers better durability, higher limits (256,000 versus 64,000 IOPS on RDS), and a lower IOPS rate of $0.065 against io1's $0.10. Migrating existing io1 volumes to io2 is roughly a 35 percent saving on the IOPS line with no functional downside, which makes it one of the cleanest database cost wins available.
How do I work out how many IOPS I need?
Take ReadIOPS plus WriteIOPS from CloudWatch at one-minute granularity over a representative fortnight, use the p99 rather than the maximum so a single backup spike does not set your provisioning, then add about 30 percent headroom for growth and for I/O bursts from index builds and vacuum. Most well-indexed OLTP databases land between 1,500 and 3,000 IOPS, inside gp3's free tier.
Should I buy IOPS or more memory?
On read-heavy workloads, memory almost always. A database at 18,000 read IOPS costs about $1,170 per month for provisioned io2 IOPS, while moving from db.r6g.xlarge to db.r6g.2xlarge adds $378 per month and lets a doubled buffer pool absorb those reads. The memory route costs under a third as much and improves latency more, since RAM beats NVMe by orders of magnitude.
When is provisioned IOPS genuinely necessary?
For write-heavy workloads whose write rate exceeds what gp3 can absorb, for latency-sensitive systems needing consistent single-digit millisecond I/O, and for very large datasets where no realistic amount of memory holds the working set. If an OLTP workload shows 20,000 IOPS, first check for a missing index or an undersized buffer pool, because buying I/O to compensate for a bad plan is the costliest fix.
How does C3X help with IOPS cost?
C3X prices storage type, provisioned IOPS, and throughput as the three separate meters they are, straight from Terraform, before you merge. That makes the difference between gp3 at $0.02 per IOPS and io1 at $0.10 visible at review time, and surfaces cases where a legacy io1 configuration has been carried forward at five times the necessary rate.
What to do next
See what an IOPS number costs before you commit to it. C3X reads your Terraform and prices your resources against a live catalog. Start with the quickstart.
Share this post
Try C3X on your own Terraform
Free and open source. No API key required. One command to install, one command to estimate.