KubePersistentVolumeFillingUp alert: what it means and how to fix it
What the KubePersistentVolumeFillingUp Prometheus alert means, its common causes, the PromQL rule and the runbook steps to fix it, with a ready-to-use rule in YAML.
Last reviewed
A PersistentVolume has less than 3% of its space left, as reported by the kubelet on the node that mounts it. Databases, queues and other stateful services usually stop accepting writes or crash once the volume is full, so this one is critical.
- Severity: Critical (
critical) - Fires after: 1m
- Part of the Kubernetes alert rules (
kubernetes.yml)
The rule
(
kubelet_volume_stats_available_bytes{job="kubelet", metrics_path="/metrics"}
/
kubelet_volume_stats_capacity_bytes{job="kubelet", metrics_path="/metrics"}
) < 0.03
and
kubelet_volume_stats_used_bytes{job="kubelet", metrics_path="/metrics"} > 0
unless on(namespace, persistentvolumeclaim)
kube_persistentvolumeclaim_access_mode{ access_mode="ReadOnlyMany"} == 1
unless on(namespace, persistentvolumeclaim)
kube_persistentvolumeclaim_labels{label_excluded_from_alerts="true"} == 1
Alert message: The PersistentVolume claimed by {{ $labels.persistentvolumeclaim }} in Namespace {{ $labels.namespace }} is only {{ $value | humanizePercentage }} free.
Common causes
- Data has simply grown: tables, indexes, topic logs or object counts outpaced the size chosen at creation.
- Retention or cleanup stopped working: log rotation, compaction, TTL jobs or backups that delete old copies.
- Temporary files, dumps or core files written to the data volume and never removed.
- The volume was sized for a test environment and reused in production.
How to fix it
- Run kubectl get pvc -n <ns> to find the PVC and PV;
- kubectl exec into a Pod using the volume and use du to find the largest directories;
- Clean up logs and temporary data;
- If the StorageClass supports online expansion, kubectl edit pvc and raise spec.resources.requests.storage; otherwise migrate the data.
Notes
- Volumes mounted read-only by many Pods (
ReadOnlyMany) are skipped. You can also silence one PVC by labelling itexcluded_from_alerts=true, but kube-state-metrics only exports PVC labels that are listed in its--metric-labels-allowlistflag, so add that label there first. - The metrics come from the kubelet (
kubelet_volume_stats_*). Volumes whose driver does not report usage statistics never produce them, so the rule stays silent for those volumes. - The expression selects
job="kubelet". If your scrape job or namespace is named differently, change these matchers or the rule never matches.
Metrics it uses
kubelet_volume_stats_available_bytes(kubelet)kubelet_volume_stats_capacity_bytes(kubelet)kubelet_volume_stats_used_bytes(kubelet)kube_persistentvolumeclaim_access_mode(kube-state-metrics)kube_persistentvolumeclaim_labels(kube-state-metrics)
Related alerts
- KubePersistentVolumeInodesFillingUp: The PersistentVolume claimed by … in Namespace … only has … free inodes.
- KubePersistentVolumeErrors: The persistent volume … has status ….
- KubeStatefulSetReplicasMismatch: StatefulSet …/… has not matched the expected number of replicas for longer than 15 minutes.
Prometheus rule YAML
Add this to a rules file loaded through rule_files, or download every Kubernetes rule at once as kubernetes.yml.
- alert: KubePersistentVolumeFillingUp
expr: |-
(
kubelet_volume_stats_available_bytes{job="kubelet", metrics_path="/metrics"}
/
kubelet_volume_stats_capacity_bytes{job="kubelet", metrics_path="/metrics"}
) < 0.03
and
kubelet_volume_stats_used_bytes{job="kubelet", metrics_path="/metrics"} > 0
unless on(namespace, persistentvolumeclaim)
kube_persistentvolumeclaim_access_mode{ access_mode="ReadOnlyMany"} == 1
unless on(namespace, persistentvolumeclaim)
kube_persistentvolumeclaim_labels{label_excluded_from_alerts="true"} == 1
for: 1m
labels:
severity: critical
annotations:
summary: "The PersistentVolume claimed by {{ $labels.persistentvolumeclaim }} in Namespace {{ $labels.namespace }} is only {{ $value | humanizePercentage }} free."
runbook_url: "https://n9e.github.io/alert-rules/kubernetes/kubepersistentvolumefillingup"
With Nightingale: this rule ships in the Kubernetes integration template; import it into a business group and Nightingale attaches the runbook to every alert it sends.
Sources and license
The rule expression is adapted from kubernetes-mixin (Apache-2.0). The runbook steps come from Nightingale's integration templates (Apache-2.0); the explanation and causes are written for this page.