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.

A volume or RDS instancewith ordered performanceCapacity, as a lineof its ownPerformance line: IOPSand throughput, billed bythe ordered figure, whetherdrawn on or notMetrics: peaks far belowthe ordered figureThe gap between orderedand consumed is the saving
The bill carries performance as a line of its own by the ordered figure, while the metric peak beside it stays far below

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

  1. 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.
  2. Lower the performance, online: AWS aws ec2 modify-volume --volume-id vol-0123456789abcdef0 --iops 6000 --throughput 250 --dry-run, then without --dry-run; on RDS aws rds modify-db-instance --db-instance-identifier <id> --iops 6000 --apply-immediately, which starts storage optimisation and keeps the instance online. Azure az 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 Cloud gcloud compute disks update <disk> --zone=<zone> --provisioned-iops=6000 for Hyperdisk, without a dry run.
  3. Where the ordered performance is needed only for a weekly window, schedule the change in both directions, within the change limits per day.
  4. Write the target values into the templates volumes and databases come from, and set BurstBalance alarms or the Consumed Percentage metrics 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.

yesnoFind the peak of the lasttwo to four weeks, backupwindows and month endincludedTarget: the peak plusa margin, not the averageLower the performance online:dry run, then the changeNeeded only for aweekly window?Schedule the change in bothdirections, within thechange limits per dayWrite the targets into thetemplates; BurstBalance orConsumed Percentage alarmsas guardsNew price from the start ofthe change; the lines fallin the next daily export
The peak of two to four weeks sets the target, the change runs online, and the templates and alarms keep the gap from returning

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 check metric on AWS and Consumed Percentage on 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.