IOPS → Throughput Estimator
IOPS × block size = throughput. 10,000 IOPS at 4 KB blocks = 40 MB/s. 10,000 IOPS at 256 KB blocks = 2,560 MB/s. Same IOPS count. Two orders of magnitude difference in throughput. The block size you benchmark at is not the block size your application uses.
Bidirectional IOPS / Throughput Converter
Bidirectional IOPS to throughput converter. Start at 4 KB baseline, then see what happens at 8 KB and 64 KB. The difference is not linear — and your EBS volume's block size may not match your benchmark.
Cloud Volume IOPS Reference
Published maxima per tier. Break-even I/O is throughput ÷ IOPS: above it the throughput ceiling binds, below it the IOPS ceiling binds. AWS quotes MiB/s; Azure quotes MB/s (106 bytes).
| Volume Tier | Max IOPS | Max Throughput | Break-even I/O |
|---|---|---|---|
| AWS EBS gp3 (Baseline) | 3,000 | 125 MiB/s | 42.7 KiB |
| AWS EBS gp3 (Max) | 80,000 | 2,000 MiB/s | 25.6 KiB |
| AWS EBS io2 (Max) | 64,000 | 1,000 MiB/s | 16 KiB |
| AWS EBS io2 Block Express | 256,000 | 4,000 MiB/s | 16 KiB |
| Azure Premium SSD v2 | 80,000 | 2,000 MB/s | 24.4 KiB |
| Azure Ultra Disk | 400,000 | 10,000 MB/s | 24.4 KiB |
| NVMe Gen4 (Single Drive) | ~1,000,000 | ~7,000 MB/s | ~7 KiB |
| NVMe Gen5 (Single Drive) | ~1,500,000 | ~14,000 MB/s | ~9.3 KiB |
IOPS vs. Throughput: The Block Size Coupling
IOPS and throughput are coupled by I/O size: Throughput = IOPS × Block Size. At the common 4 KB filesystem block boundary, 10,000 IOPS ≈ 40 MB/s. At 64 KB — typical for sequential read workloads like video streaming or log ingestion — the same 10,000 IOPS delivers 640 MB/s. This is why database workloads (small random I/O) are IOPS-bound while media serving (large sequential I/O) is throughput-bound.
When provisioning cloud volumes, the IOPS-to-throughput ratio is critical, and every major vendor fixes it at almost exactly 0.25 MiB/s per provisioned IOPS — which means one IOPS is worth 256 KiB per second. The column above is that exchange rate applied to each tier's two published ceilings: divide throughput by IOPS and you get the I/O size at which both run out at once. Above it the throughput ceiling binds and provisioned IOPS stops being spendable; below it the IOPS ceiling binds and throughput headroom is wasted. gp2 is the instructive exception, and not for the reason usually given: its IOPS scale at 3 IOPS per GiB of volume size up to 16,000, with throughput capped absolutely at 250 MiB/s, so its ratio is a function of volume size rather than a constant. Use this estimator to validate your tier selection before deployment, and read the full analysis of the 0.25 rule before provisioning 80,000 IOPS for a workload with large I/Os.
The two ceilings: IOPS and throughput are separate buckets
Throughput = IOPS × block size is the whole relationship, but it hides the provisioning detail that costs money: cloud volumes limit IOPS and throughput independently, and the lower ceiling wins. A gp3 volume can be configured to 80,000 IOPS and 2,000 MiB/s simultaneously. Ask for 80,000 IOPS at a 64 KB block size and you need 5,000 MiB/s — the IOPS bucket has room, the throughput bucket does not, and the volume caps at 2,000 MiB/s, delivering 32,000 IOPS instead of the 80,000 you provisioned. Sixty percent of the IOPS line item is unusable at that block size.
Work the numbers at the common block sizes to see how far apart the workable ranges sit: 10,000 IOPS is 40 MB/s at 4 KB, 160 MB/s at 16 KB, and 640 MB/s at 64 KB. The same IOPS figure describes a modest database and a media server, and nothing in the label "IOPS" tells you which one you are running.
IOPS is a concurrency problem, not a bandwidth problem
This is the part that catches single-threaded applications. A 4 KB random read on a cloud volume completes in roughly 1 ms on average. One outstanding request therefore yields at most 1,000 IOPS — the ceiling is the queue, not the disk. Reaching 80,000 IOPS takes about 80 requests in flight at once. An application issuing synchronous, one-at-a-time reads cannot consume the provisioned IOPS no matter what the volume is rated for, and paying for 80,000 IOPS to serve a synchronous workload buys latency you will never see.
The diagnostic follows directly: if iostat shows average queue depth near 1 while measured IOPS sits far below provisioned IOPS, the bottleneck is the application's I/O model. If queue depth is high and IOPS still falls short, you have hit a real ceiling and provisioning will help.
Worked example: why WAL writes need a different volume
PostgreSQL writes WAL in 8 KB sequential records. Sustaining 500 MB/s of WAL throughput requires 500,000 KB ÷ 8 KB = 62,500 IOPS at that block size. On today's gp3 that figure sits inside the 80,000 IOPS ceiling — but only just, because 8 KB is well below the 25.6 KiB break-even, so the IOPS ceiling is the one that binds and you need all 62,500 provisioned, which in turn requires a volume of at least 125 GiB at 500 IOPS per GiB. Two years ago the same arithmetic exceeded gp3's 16,000 IOPS ceiling by nearly four times, and the standing advice was to move WAL to io2. The advice outlived the limit that produced it: gp3's ceiling quadrupled, the break-even size barely moved, and a recommendation copied from an older article now over-provisions by a factor of four.
The gp2 heritage trap
Older guidance dies slowly, and gp2 is where most of it comes from. A gp2 volume scales at 3 IOPS per GiB of volume size, from a floor of 100 up to 16,000 IOPS at 5,334 GiB, and its throughput is exactly the workload's: AWS gives the formula throughput in MiB/s = IOPS × I/O size in KiB / 1,024, capped absolutely at 250 MiB/s. A 1 TiB gp2 volume therefore provisions 3,072 IOPS, and at 256 KiB I/Os that formula would predict 768 MiB/s, which the 250 MiB/s cap then cuts down. Note what the cap means: gp2's break-even size is 16 KiB at its ceiling, so a large-block workload on gp2 is throughput-bound at an I/O size one tenth of what a modern gp3 will carry. gp3 decoupled the two dials — 3,000 IOPS and 125 MiB/s at baseline, each scalable independently, subject to the 0.25 MiB/s per IOPS ratio — which is the actual improvement, not a change in the multiplier.