Provisioned IOPS cost arises wherever performance is ordered rather than measured: io2 and Ultra Disk IOPS, Hyperdisk Extreme IOPS, the throughput of a Premium SSD v2 and the provisioned IOPS of an RDS instance are billed by the ordered figure, whether the application draws on them or not. The saving comes from the gap between ordered and consumed, which the providers' metrics show per volume, and it is taken with an online change and no restart. The fix belongs to the team that runs the database or service on the volume, because only there is the peak load known that has to stay.
Symptom
The bill carries performance as a line of its own beside capacity. On AWS these are lineItem/UsageType EUC1-EBS:VolumeP-IOPS.io2 and EBS:VolumeP-IOPS.piops for io1, EBS:VolumeIOPS.gp3 and EBS:VolumeThroughput.gp3 for performance above the gp3 baseline, and on RDS RDS:PIOPS-Storage and the IOPS lines of gp3 storage. On Azure Provisioned IOPS and Provisioned Throughput (MBps) sit as rows of their own under Azure Premium SSD v2 and Ultra Disks, per 1/Hour. On Google Cloud Hyperdisk Extreme IOPS or Hyperdisk Balanced IOPS and Throughput carry performance as a SKU beside capacity. Beside them lie metrics whose peaks stay far below the ordered figure. The types and their pricing dimensions are in block, object and file storage; the type change itself in modernising volume types.
How to detect it
AWS
The volumes with ordered performance:
aws ec2 describe-volumes --filters Name=volume-type,Values=io1,io2,gp3 \
--query 'Volumes[?Iops>`3000` || Throughput>`125`].[VolumeId,VolumeType,Size,Iops,Throughput,Attachments[0].InstanceId]' --output table
One volume's used performance over two weeks, as a per-minute sum of VolumeReadOps and VolumeWriteOps:
aws cloudwatch get-metric-statistics --namespace AWS/EBS --metric-name VolumeWriteOps \
--dimensions Name=VolumeId,Value=vol-0123456789abcdef0 --statistics Maximum \
--start-time 2026-10-07T00:00:00Z --end-time 2026-10-21T00:00:00Z --period 60
The maximum per minute divided by 60 is the peak in IOPS. Compute Optimizer rates type, size, IOPS and throughput from 14 days of metrics and names, for EC2 instances, the reasons EBS IOPS over-provisioned and EBS throughput over-provisioned; aws compute-optimizer get-ebs-volume-recommendations returns the volume recommendations with target values. RDS instances with provisioned IOPS:
aws rds describe-db-instances \
--query 'DBInstances[?Iops!=null].[DBInstanceIdentifier,StorageType,AllocatedStorage,Iops,StorageThroughput,DBInstanceClass]' --output table
The peak of ReadIOPS plus WriteIOPS in the AWS/RDS namespace against Iops is the gap; Trusted Advisor carries "Amazon RDS cost optimization recommendations for DB instance storage" and the older check "Amazon EBS over-provisioned volumes" for it (read on 2026-10-21). In billing data the VolumeP-IOPS and PIOPS-Storage lines per resource are the figure (AWS Cost and Usage Report).
Azure
The disks with separately ordered performance:
az disk list --query "[?sku.name=='PremiumV2_LRS' || sku.name=='UltraSSD_LRS'].{name:name, sku:sku.name, gb:diskSizeGb, iops:diskIopsReadWrite, mbps:diskMBpsReadWrite, vm:managedBy}" -o table
Azure Monitor provides per VM the metrics Data Disk IOPS Consumed Percentage and Data Disk Bandwidth Consumed Percentage, which set actual against provisioned performance per disk, split by LUN (disk metrics):
az monitor metrics list --resource <vm-resource-id> --metric "Data Disk IOPS Consumed Percentage" \
--aggregation Maximum --interval PT1H --start-time 2026-10-07T00:00:00Z --end-time 2026-10-21T00:00:00Z
A maximum well below 100 percent over two weeks is the gap. For Premium SSD with a fixed tier the same holds through the tier change; a Premium SSD's performance tier can be lowered without a size change. Azure Advisor has no recommendation on ordered IOPS. In the cost export the rows Provisioned IOPS and Provisioned Throughput (MBps) per disks/ resource are the figure (Azure cost exports).
Google Cloud
The disks with ordered IOPS or throughput:
gcloud compute disks list --filter="provisionedIops>0 OR provisionedThroughput>0" \
--format="table(name,zone.basename(),type.basename(),sizeGb,provisionedIops,provisionedThroughput,users.list())"
Used performance comes from the metrics compute.googleapis.com/instance/disk/read_ops_count and write_ops_count per disk in Cloud Monitoring; the sum per minute divided by 60 against provisionedIops is the gap. There is no recommender for it. In the billing export the IOPS and Throughput SKUs per resource.name are the figure (Google Cloud billing export).
How to fix it
- Find the peak of the last two to four weeks, backup windows and month end included, and set the target performance as the peak plus a margin; what the application needs on average is not the yardstick.
- Lower the performance, online: AWS
aws ec2 modify-volume --volume-id vol-0123456789abcdef0 --iops 6000 --throughput 250 --dry-run, then without--dry-run; on RDSaws rds modify-db-instance --db-instance-identifier <id> --iops 6000 --apply-immediately, which starts storage optimisation and keeps the instance online. Azureaz disk update --name <disk> --resource-group <rg> --disk-iops-read-write 6000 --disk-mbps-read-write 250, on Premium SSD v2 up to four times in 24 hours without detaching; on Premium SSD the performance tier with--tier. Google Cloudgcloud compute disks update <disk> --zone=<zone> --provisioned-iops=6000for Hyperdisk, without a dry run. - Where the ordered performance is needed only for a weekly window, schedule the change in both directions, within the change limits per day.
- Write the target values into the templates volumes and databases come from, and set
BurstBalancealarms or theConsumed Percentagemetrics as guards against too little performance.
The new price applies from the start of the change; the IOPS and throughput lines fall in the next daily export.
Risks and side effects
- Too few IOPS shows as latency, not as an error: queues grow, databases answer more slowly, backups run longer. The
IOPS exceeded checkmetric on AWS andConsumed Percentageon Azure show when the limit is hit. - The peak often lies outside the lookback period: year end, a migration, a restore. The margin belongs in the target, and the way back up has to be rehearsed before the cut.
- Change limits: on AWS four times in 24 hours per volume and minutes to hours per change, on Azure twice a day for the type and four times in 24 hours for a Premium SSD v2's performance; a cut that completes on Friday cannot be reversed at once on Monday.
- On RDS performance applies per instance; a Multi-AZ instance needs it on both sides, and lowering it starts a storage optimisation during which no further storage change is possible.
- The gp3 baseline cannot be undercut; a volume that does not use 3,000 IOPS saves nothing from a smaller figure.
How to verify the saving
Before: per volume or instance, the lines VolumeP-IOPS, VolumeIOPS, VolumeThroughput, PIOPS-Storage, Provisioned IOPS, Provisioned Throughput and the IOPS SKUs in the last full month, as amount and ordered quantity, beside the measured peak. After: the same lines in the first full month with the new quantity, with an unchanged capacity line and unchanged service latency. The difference from the recommender or the price list is the estimate, the team's release makes it accepted, the change implemented, and the following month's invoice realises it in the pattern of estimated, accepted, implemented, verified. The ratio of used to ordered performance per volume is the figure that then sits regularly in the cost review.
Sources
Read on 2026-10-21. Compute Optimizer EBS recommendations, Compute Optimizer EC2 recommendations, Modify a volume with Elastic Volumes, Trusted Advisor cost optimization checks, Azure disk metrics, Azure disk types, Azure, convert disk types, gcloud compute disks list.