Kubernetes persistent volume cost: the storage bill nobody reads
PersistentVolumeClaims quietly provision real cloud disks, and those disks keep billing long after the pod is gone. Storage class choice, over-sized claims, and orphaned volumes routinely add 15 to 25 percent to a cluster bill. Here are the numbers.
Quick answer
Every PersistentVolumeClaim provisions a real cloud disk billed per GB-month on provisioned size, not used size. AWS gp3 is $0.08 per GB-month, gp2 is $0.10, io2 is $0.125 plus $0.065 per provisioned IOPS. Azure Premium SSD P10 at 128 GiB is about $19.71 per month, GCP balanced persistent disk is $0.10 per GB-month and SSD is $0.17. The three big wastes are over-sized claims (a 500 GB PVC holding 40 GB costs $40 per month for $3.20 of data), premium classes used by default, and volumes orphaned by a Retain reclaim policy. Storage is typically 10 to 25 percent of a stateful cluster bill.
Node cost gets all the attention in Kubernetes because it is the biggest line. Storage gets none, which is why it is usually the messiest. A PersistentVolumeClaim is a one-line YAML request that creates a durable, billable cloud disk, and nothing in the cluster ever tells you it is 8 percent full.
What a volume actually costs
| Storage class backing | Price | 100 GB per month |
|---|---|---|
| AWS gp3 | $0.08 per GB-month | $8.00 |
| AWS gp2 | $0.10 per GB-month | $10.00 |
| AWS io2 | $0.125 per GB-month plus $0.065 per IOPS | $12.50 plus IOPS |
| AWS st1 throughput HDD | $0.045 per GB-month | $4.50 |
| Azure Standard SSD E10 | fixed tier | about $9.60 |
| Azure Premium SSD P10 | fixed tier | about $19.71 |
| GCP pd-standard | $0.04 per GB-month | $4.00 |
| GCP pd-balanced | $0.10 per GB-month | $10.00 |
| GCP pd-ssd | $0.17 per GB-month | $17.00 |
Two things jump out. First, the spread between the cheapest and most expensive class for the same capacity is over 4 times. Second, AWS gp3 is 20 percent cheaper than gp2 for the same size and includes 3,000 IOPS and 125 MB/s baseline, where gp2 ties IOPS to size. Any cluster still defaulting its storage class to gp2 is paying a 20 percent premium for worse baseline performance. A cluster with 40 TB of gp2 volumes saves $800 per month by switching the default to gp3.
You pay for provisioned, not used
This is the core trap. A StatefulSet template that requests 500 GB per replica creates a 500 GB disk for every replica, immediately, regardless of how much data lands on it. Nine replicas is 4.5 TB provisioned. If the real data footprint is 400 GB total, you are paying $360 per month on gp3 for $32 of storage.
| Scenario | Provisioned | Used | gp3 monthly cost | Cost of used data |
|---|---|---|---|---|
| 9-replica StatefulSet, 500 GB claim | 4,500 GB | 400 GB | $360 | $32 |
| Same, 100 GB claim with expansion | 900 GB | 400 GB | $72 | $32 |
The fix is volume expansion. Set allowVolumeExpansion to true on the StorageClass, start claims small, and grow them when monitoring says so. Expansion is online on all three major clouds for most classes. The catch is that you cannot shrink, so err small and grow rather than provisioning for a three-year projection on day one.
Orphaned volumes are the silent line
When a PVC is deleted, what happens to the underlying disk depends on the StorageClass reclaim policy. With Delete, the disk goes away. With Retain, the PersistentVolume moves to Released and the cloud disk keeps existing and keeps billing, forever, with no pod, no claim, and no dashboard mentioning it.
Retain is the right choice for databases where accidental deletion would be catastrophic. It is the wrong default for everything else, and it is set as a cluster-wide default in plenty of platforms. The audit is simple: list PersistentVolumes in Released status, cross-reference against cloud disks with no attachment, and check snapshots. Teams running this audit for the first time commonly find 2 to 8 TB of orphaned volumes, $160 to $640 per month of pure waste. This is the Kubernetes version of the unattached disk problem that plagues every cloud account.
Snapshots compound quietly
Volume snapshots are billed separately and incrementally. AWS EBS snapshots are $0.05 per GB-month for the standard tier. A nightly snapshot policy with 30-day retention on 4.5 TB of volumes, where daily change is 3 percent, accumulates roughly 4.5 TB of base plus 4 TB of deltas, about $425 per month. Nothing is wrong with that if you need 30 days of recovery points. Plenty of clusters keep 90 days by accident because the policy was never revisited, which triples it.
Practical controls
Set the cluster default StorageClass to the cheapest class that meets baseline needs, gp3 on AWS, pd-balanced on GCP, Standard SSD on Azure, and make premium classes opt-in with a reason. Enable allowVolumeExpansion everywhere. Use Delete as the default reclaim policy and Retain only on explicitly marked classes for databases. Put a ResourceQuota on requests.storage per namespace so a team cannot provision 20 TB by accident. And run a quarterly sweep for Released PVs and unattached cloud disks.
Storage classes and volume sizes live in Terraform and Helm values, which means they are reviewable before they are billable. Price the storage class and claim size against theresource catalog so a 500 GB per replica default gets challenged in the pull request rather than discovered a year later. It pairs naturally with trackingcost per namespace, since storage is one of the few Kubernetes costs that attributes cleanly to a single team.
FAQ
How much do Kubernetes persistent volumes cost?
They cost whatever the underlying cloud disk costs, billed on provisioned size not used size. AWS gp3 is $0.08 per GB-month, gp2 is $0.10, and io2 is $0.125 plus $0.065 per provisioned IOPS. GCP pd-standard is $0.04, pd-balanced $0.10, and pd-ssd $0.17 per GB-month. Azure Premium SSD P10 at 128 GiB runs about $19.71 per month. Storage is typically 10 to 25 percent of a stateful cluster bill.
Do I pay for unused space in a PersistentVolumeClaim?
Yes. Cloud disks bill on provisioned capacity, so a 500 GB claim holding 40 GB costs the full 500 GB rate. A nine-replica StatefulSet with 500 GB claims provisions 4.5 TB at $360 per month on gp3 even if the real data footprint is 400 GB, worth $32. Enable allowVolumeExpansion, start claims small, and grow them as monitoring requires, since disks can be expanded online but never shrunk.
What happens to the disk when I delete a PVC?
It depends on the StorageClass reclaim policy. With Delete, the underlying cloud disk is removed. With Retain, the PersistentVolume enters Released status and the cloud disk keeps existing and billing indefinitely with no pod, no claim, and no dashboard showing it. Teams auditing this for the first time commonly find 2 to 8 TB of orphaned volumes, $160 to $640 per month of pure waste.
Should I switch my Kubernetes storage class from gp2 to gp3?
Almost always yes on AWS. gp3 costs $0.08 per GB-month versus $0.10 for gp2, a 20 percent saving, and includes 3,000 IOPS and 125 MB/s baseline where gp2 ties IOPS to volume size. A cluster with 40 TB of gp2 volumes saves about $800 per month by moving the default class to gp3, usually with better baseline performance for small volumes.
How much do Kubernetes volume snapshots cost?
AWS EBS snapshots are $0.05 per GB-month for the standard tier and bill incrementally. A nightly policy with 30-day retention on 4.5 TB of volumes changing 3 percent daily accumulates roughly 8.5 TB of snapshot data, about $425 per month. Retention policies that drift to 90 days without review triple that, so revisit snapshot schedules alongside volume sizing.
How does C3X help with Kubernetes storage cost?
Storage classes, claim sizes, and reclaim policies live in Terraform and Helm values, so C3X prices them against a live catalog before merge. A StatefulSet template requesting 500 GB per replica shows its full provisioned monthly cost in the pull request, which is when someone can ask whether 100 GB with expansion enabled would do instead.
What to do next
See what every PersistentVolumeClaim will cost before it is created. 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.