Storage
Every disk on the bill, matched to the volume that uses it
KubeOn joins each billed disk to its PersistentVolume, claim and cluster. Disks that outlived their volume, volumes far larger than their data, and disks on an older storage class are each priced on your bill and written up as a change your team can review.
Storage workflow
Who does what
From a forgotten disk to a safe cleanup
Follow the inputs, decision and output for this workflow.
- KubeOn, automated
- Your decision
- Your systems and people
- Step 1, Read the bill, by Billing source:
Every billed disk, with its size, type and daily cost, from the billing export.
- Step 2, Join to volumes, by KubeOn hub:
Match each disk to its PersistentVolume, claim and cluster, and read kubelet volume usage.
- Step 3, Review, by Platform team:
Check the evidence: no volume for a day or more, a cluster still reporting, the data owner agreed.
- Step 4, Snapshot + delete, by Your pipeline:
Run the snapshot-then-delete or migration steps through your own tooling.
KubeOn reads and recommends. Your team and pipeline change the cluster.
Read the workflowWho does what
From a forgotten disk to a safe cleanup
Follow the inputs, decision and output for this workflow.
- 01
Read the bill
Every billed disk, with its size, type and daily cost, from the billing export.
Billing source
- 02
Join to volumes
Match each disk to its PersistentVolume, claim and cluster, and read kubelet volume usage.
KubeOn hub
- 03
Review
Check the evidence: no volume for a day or more, a cluster still reporting, the data owner agreed.
Platform team
- 04
Snapshot + delete
Run the snapshot-then-delete or migration steps through your own tooling.
Your pipeline
KubeOn reads and recommends. Your team and pipeline change the cluster.
Read the workflowThe problem
Storage keeps billing after the workload is gone
Deleted clusters leave disks
A PersistentVolume with a Retain policy, or a cluster torn down by hand, leaves its disks billing every month.
Nobody knows what is safe
A disk with no owner might hold the only copy of something. So it stays, and it bills.
Sized for a guess
Claims are sized when a service is created and rarely revisited, while the data inside stays small.
The default class never changed
Clusters created years ago still provision gp2 by default, at a higher price than gp3 for the same size.
Orphaned and released volumes
Disks that outlived their volume, with the evidence to delete them
A disk still billing a day or more after its PersistentVolume disappeared, from a cluster that is still reporting, is orphaned. A PV left in Released, Available or Failed with a disk that still bills is reclaimable. Disks with no PV and no cluster tag are shown as possible orphans, an upper bound kept out of the total until someone checks them.
- Billing line, PV history and cluster heartbeat side by side
- Snapshot-then-delete steps that skip any disk no longer available
- Possible orphans listed separately, never counted as savings
Over-provisioned claims
Claims sized to their data, with room to grow
With Prometheus connected, KubeOn reads each volume's used bytes from the kubelet every hour. After 14 days with at least 24 samples, a volume with 30% or more reclaimable gets a recommended size of its peak times 1.5, never below 1 GiB.
- Peak use over 14 days, not today's figure
- 50% headroom above the peak
- Shrinking needs a new volume and a copy, so the steps say so
# prod-use1-a / data-postgres-0: peak 118 GiB of 500 GiB over 14 days# A volume cannot shrink in place: create the smaller claim, copy, then switch.apiVersion: v1kind: PersistentVolumeClaimmetadata: name: data-postgres-0-v2spec: storageClassName: gp3 accessModes: ["ReadWriteOnce"] resources: requests: storage: 180Gi # max(peak x 1.5, 1 GiB), rounded up: $25.60/mo lessStorage classes
gp2 to gp3, priced per volume and checked for throughput
On AWS, gp3 costs less per GB-month than gp2 and includes 3,000 IOPS and 125 MiB/s at any size. KubeOn prices every gp2 volume at the gp3 rate. Volumes up to 170 GiB move as they are; larger ones can get more throughput from gp2, so they are flagged for a check of their peak throughput first.
- Changed in place with Elastic Volumes, without detaching
- A gp3 StorageClass so new claims stop landing on gp2
- Per-volume saving at your billed rate
# Existing gp2 volumes up to 170 GiB, changed in place (no detach)for vol in vol-0a41c8e2d9f3b6170 vol-0b7d2e9a4c6f81352; do aws ec2 modify-volume --volume-id "$vol" --volume-type gp3 aws ec2 describe-volumes-modifications --volume-ids "$vol" \ --query 'VolumesModifications[0].ModificationState' --output textdoneStop new waste
Fix the default, not only the volumes
Migrating today's volumes does not help if tomorrow's claims still land on the old class. Each storage-class finding comes with the StorageClass change, so new volumes are created on the cheaper class through your own GitOps repository.
- Default StorageClass as a reviewed change
- WaitForFirstConsumer, so volumes are created in the pod's zone
- Volume expansion enabled for later growth
# New volumes on gp3 from now onapiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: gp3 annotations: storageclass.kubernetes.io/is-default-class: "true"provisioner: ebs.csi.aws.comparameters: type: gp3volumeBindingMode: WaitForFirstConsumerallowVolumeExpansion: trueStorage rules
Every storage finding, with the rule behind it
Savings are monthly, at the billed cost of each disk, and recomputed with the rest of your savings every 30 minutes.
| Finding | Rule | Counted in savings |
|---|---|---|
| Orphaned volume | Billing 24 hours or more after its PV disappeared, from a cluster still reporting, and at least $0.50 over 7 days | Yes |
| Released volume | PV in Released, Available or Failed with a backing disk still billing $0.50 or more a month | Yes |
| Over-provisioned volume | At least 24 hourly samples over 14 days; max(peak x 1.5, 1 GiB) when 30% or more is reclaimable | Yes |
| Older storage class | AWS gp2 volume priced at the gp3 rate for the same size; volumes over 170 GiB flagged for a throughput check | Up to 170 GiB |
| Orphaned file system volumes | Released PVs grouped by file system; cost estimated as an even share of the file system | Yes |
| Possible orphans | Billed daily for 7 days with no PV and no cluster tag, in non-production accounts | No, an upper bound |
KubeOn never deletes or modifies a disk. Every finding becomes a change proposal with the script or manifest, and your team runs it.
Capabilities
Storage cleanup your platform team can trust
Billing to volume join
Every billed disk matched to its PV, claim and cluster.
Volume history
When a PV disappeared, and whether its cluster still reports.
Used bytes
Kubelet volume stats over 14 days, from Prometheus.
Storage classes
gp2 volumes priced at gp3, with a throughput check for large ones.
Snapshot first
Scripts snapshot each disk and skip any that are attached again.
Proposals
Each finding is a reviewable change with a risk level and owner.
FAQ
Storage, answered
No. KubeOn has read-only access to your billing and clusters. It writes the snapshot-then-delete or migration steps, and your team runs them after review. The scripts check each disk's state again before touching it.
Related
Evaluate storage with your team.
Bring your platform and FinOps priorities. We will walk through the data, controls and evidence for your evaluation.