You deploy a stateful application on Kubernetes—PostgreSQL, RabbitMQ, or perhaps a custom data store. You carefully define a PersistentVolumeClaim (PVC), bind it to a PersistentVolume (PV), and confirm the data survives a pod restart. Then a cluster node fails, or you perform a routine upgrade, and the data is gone. This scenario is so common that practitioners have given it a name: the persistent volume illusion. The PV still exists in the API, but the data it was supposed to protect has vanished. This article explains why this happens and how to close the gap between expectation and reality.
Where the Illusion Bites: Field Context
The persistent volume illusion shows up in several real-world situations. The most painful is during cluster upgrades. A team schedules a Kubernetes version bump, drains nodes, and expects stateful workloads to resume seamlessly. Instead, they find that PVs are in a Released state, the underlying storage has been deleted, or the new nodes cannot attach the volume because of topology constraints. Another common scenario is when a StatefulSet is deleted and recreated—perhaps as part of a CI/CD pipeline for a staging environment. The PVCs are deleted along with the pods, and unless the reclaim policy is set to Retain, the PV and its data are gone.
Teams also encounter this illusion when using cloud provider storage classes that default to Delete. A developer creates a PVC for a development database, works for a week, then deletes the pod to redeploy. The PVC is deleted, the PV is deleted, and the data is gone. The developer assumed the volume was persistent because the PVC was defined as a separate resource, but the lifecycle is tied to the pod or StatefulSet. In production, the same mistake occurs when a Helm chart upgrade deletes and recreates a PVC, or when a cluster autoscaler terminates a node without respecting volume attachments.
A third field context is when teams use hostPath or local volumes without understanding the node affinity. A pod using a hostPath volume runs on node A, writes data, then is rescheduled to node B. The data remains on node A, and the pod on node B starts fresh. The PV appears in the cluster, but the data is inaccessible. This is not a bug—it is the intended behavior of hostPath—but it surprises teams who expect network-attached storage semantics.
Finally, the illusion appears in disaster recovery exercises. A team backs up etcd and PV manifests, then restores to a new cluster. The PVs are created, but they point to cloud disks that no longer exist or have different IDs. The data is not actually restored; the PV is a shell pointing to nothing. Understanding these contexts is the first step to building a robust stateful infrastructure.
The Gap Between PV and Actual Storage
A PersistentVolume in Kubernetes is a resource object that represents a piece of storage in the cluster. It has a status, a reclaim policy, and a reference to a storage backend (like an EBS volume or GCE persistent disk). But the PV is not the storage itself—it is a pointer. When the PV is deleted, the underlying storage may or may not be deleted depending on the reclaim policy. Many teams assume that creating a PV means the data is safe, but the PV is only as durable as the policy and the storage backend's lifecycle.
Foundations Readers Confuse: Lifecycle and Reclaim Policies
Three concepts are frequently misunderstood: the PVC lifecycle, the PV reclaim policy, and the volume mode. Let's clarify each.
The PVC lifecycle is tied to the pod or StatefulSet that uses it. When a pod is deleted, the PVC is not automatically deleted unless it was created as part of a StatefulSet's volumeClaimTemplate and the StatefulSet is scaled down or deleted. Even then, the behavior depends on the PVC deletion policy in the StatefulSet spec. By default, StatefulSet does not delete PVCs when the pod is deleted—that is a safety measure. But if a team manually deletes the PVC, or if a Helm chart is configured to delete PVCs on uninstall, the data is lost.
The PV reclaim policy determines what happens to the underlying storage when the PVC is deleted. There are three policies: Retain, Delete, and Recycle (deprecated). Retain keeps the storage volume and the PV in a Released state, allowing manual reclamation. Delete removes both the PV and the underlying storage. Recycle was used for basic scrubbing but is deprecated in favor of dynamic provisioning. Many cloud provider StorageClasses default to Delete, which means that deleting a PVC deletes the cloud disk. Teams must explicitly set the reclaim policy to Retain for critical data, or use a StorageClass that supports Retain.
Volume mode is another source of confusion. Persistent volumes can be Filesystem or Block mode. Filesystem mode mounts the volume as a directory, while Block mode exposes it as a raw block device. If a pod expects a filesystem but the volume is in Block mode, the mount fails, and the data may appear inaccessible. This is not data loss, but it looks like it. Similarly, access modes (ReadWriteOnce, ReadOnlyMany, ReadWriteMany) must match the pod's requirements. A pod that tries to mount a ReadWriteOnce volume on multiple nodes will fail, and the data remains on the original node.
The Role of Storage Classes
StorageClasses define the provisioner, parameters, and reclaim policy for dynamically provisioned PVs. When a PVC requests a StorageClass, the provisioner creates a PV and binds it. The reclaim policy in the StorageClass is copied to the PV at creation time. Changing the StorageClass later does not affect existing PVs. Teams often assume that updating the StorageClass will change the reclaim policy of existing volumes, but it does not. Each PV retains the policy it was created with.
Patterns That Usually Work: StatefulSets and volumeClaimTemplates
For stateful workloads, the recommended pattern is to use a StatefulSet with a volumeClaimTemplate. This ensures that each replica gets its own PVC and PV, and the PVCs are named predictably. When a pod is rescheduled, the StatefulSet controller ensures the new pod mounts the same PVC, preserving data. This pattern works well for databases, message queues, and any application that requires stable storage and network identity.
Another pattern that works is using a separate PVC per pod, created outside the StatefulSet, and referencing it in the pod spec. This gives more control over the PVC lifecycle but requires manual management. It is useful for applications that need custom storage configurations or when using existing PVs.
For workloads that need shared storage across pods, a ReadWriteMany volume backed by NFS or a cloud-native filesystem (like Amazon EFS or Azure Files) is appropriate. These volumes can be mounted by multiple pods simultaneously, but they have performance and consistency trade-offs. They are best for content management systems, analytics pipelines, or any workload where concurrent reads and writes are acceptable.
Finally, for development and test environments where data loss is acceptable, using emptyDir with a memory-backed medium or a temporary PV with Delete reclaim policy is fine. The key is to match the storage pattern to the data criticality. Do not use the same pattern for production databases as for ephemeral caches.
Testing Persistence Safely
To verify that your stateful setup is truly persistent, perform a controlled test. Create a StatefulSet with a volumeClaimTemplate, write a test file to the volume, delete the pod (not the StatefulSet), and confirm the new pod mounts the same volume and the file is present. Then scale down the StatefulSet to zero, scale back up, and check again. Finally, delete the StatefulSet and PVCs manually to ensure the reclaim policy works as expected. Document the results and share them with the team.
Anti-Patterns and Why Teams Revert
Several anti-patterns cause teams to lose data and then revert to simpler but less reliable setups. The most common is using emptyDir for data that should survive restarts. emptyDir is ephemeral—data is lost when the pod is deleted or rescheduled. Teams sometimes use emptyDir because it is simple and fast, but they forget to migrate to persistent storage when the application becomes stateful. The result is data loss during any pod restart.
Another anti-pattern is relying on hostPath volumes without node selectors or node affinity. hostPath mounts a directory from the host node's filesystem. If the pod is rescheduled to a different node, the data is not available. Teams often use hostPath for simplicity in development, then promote the same configuration to production without adding node constraints. The data appears to be persistent because it survives a pod restart on the same node, but it fails during node failures or scaling events.
A third anti-pattern is deleting PVCs manually or through automation without checking the reclaim policy. A CI/CD pipeline that tears down an environment by deleting all PVCs will lose data if the reclaim policy is Delete. Teams often assume that the data is backed up elsewhere, but backups are frequently incomplete or untested.
Finally, teams sometimes use StatefulSets without a volumeClaimTemplate, relying on a single shared PVC for all replicas. This works only if the PVC supports ReadWriteMany. If the PVC is ReadWriteOnce, only one pod can mount it at a time, and the others will fail to start. This causes confusion and downtime. The fix is to use volumeClaimTemplate to give each replica its own PVC.
Why Teams Revert to Simpler Setups
When these anti-patterns cause data loss, teams often revert to running stateful workloads outside Kubernetes—on virtual machines or bare metal—because they perceive Kubernetes storage as unreliable. This is a mistake. The problem is not Kubernetes storage; it is the configuration. With proper understanding of PVC lifecycles, reclaim policies, and StatefulSet patterns, Kubernetes storage is reliable. The key is to invest in learning these concepts and testing them thoroughly.
Maintenance, Drift, and Long-Term Costs
Over time, storage configurations drift from their original design. A team may change a StorageClass's reclaim policy, but existing PVs retain the old policy. New PVs use the new policy, creating inconsistency. Teams may also add new storage backends without updating documentation, leading to confusion about which volumes are safe to delete.
Long-term costs include orphaned PVs that are in a Released state but still consume cloud storage. These orphaned volumes incur charges without providing value. The cost can be significant if the team forgets to reclaim or delete them. Another cost is the time spent debugging data loss incidents. Each incident requires root cause analysis, which could be avoided with proper configuration.
Maintenance tasks include periodically auditing PVs and PVCs, checking reclaim policies, and verifying that backups are working. Tools like kube-state-metrics and custom Prometheus rules can alert on PVs with Delete policy that are used by critical workloads. Teams should also review StorageClass parameters and ensure that new projects use the correct classes.
Another long-term cost is the complexity of multi-attach volumes. Some cloud providers support ReadWriteMany, but performance degrades with many concurrent writers. Teams may need to implement application-level sharding or use a distributed filesystem. The cost of managing this complexity should be weighed against the benefit of shared storage.
Drift Example: Changed StorageClass
Consider a team that initially used a StorageClass with reclaim policy Delete for all environments. After a data loss incident, they changed the StorageClass to Retain. New PVCs use Retain, but existing PVs still use Delete. Six months later, a developer deletes a PVC that was created with the old StorageClass, and the data is lost. The team blames Kubernetes, but the root cause is the drift between the StorageClass and the existing PVs.
When Not to Use This Approach
Not every workload needs persistent volumes. If the application is stateless—like a web server that reads from a database or a worker that processes messages—use emptyDir or ephemeral volumes. Adding persistent storage adds complexity and cost without benefit.
For workloads that need high throughput and low latency, consider using local SSDs with node affinity and a DaemonSet to manage data placement. Local volumes are faster than network-attached storage but require careful scheduling to avoid data loss on node failure. This approach is appropriate for caching layers or high-performance databases, but it requires a robust backup strategy.
For workloads that need multi-region availability, consider using a cloud-managed database service instead of self-managing stateful containers. Services like Amazon RDS, Google Cloud SQL, or Azure Database handle replication, failover, and backups. Running a database on Kubernetes is possible but adds operational overhead. The decision should be based on the team's expertise and the application's requirements.
Finally, avoid using persistent volumes for ephemeral data like logs or temporary files. Use a log aggregator or a sidecar container to ship logs to a central store. Persistent volumes for logs are expensive and unnecessary unless retention policies require it.
Decision Framework: When to Use Persistent Volumes
Use persistent volumes when: (1) the application writes data that must survive pod restarts and rescheduling, (2) the data is not replicated elsewhere, and (3) the team has the operational capacity to manage backups and volume lifecycle. Do not use persistent volumes when: (1) the data can be regenerated, (2) the application is stateless, or (3) the team lacks experience with Kubernetes storage.
Open Questions / FAQ
Q: Does using a StatefulSet guarantee data persistence?
A: No. StatefulSet ensures stable network identity and ordered deployment, but data persistence depends on the PVC lifecycle and reclaim policy. If the PVC is deleted, the data is gone. StatefulSet does not protect against PVC deletion.
Q: How do I change the reclaim policy of an existing PV?
A: You can edit the PV object and change the `persistentVolumeReclaimPolicy` field from Delete to Retain. This does not affect the underlying storage. After the change, if the PVC is deleted, the PV will enter Released state instead of being deleted. You can then manually reclaim the storage by deleting the PV and re-creating it with the same storage ID.
Q: What is the difference between dynamic and static provisioning?
A: Dynamic provisioning creates PVs automatically when a PVC requests a StorageClass. Static provisioning requires an administrator to create PVs in advance. Dynamic provisioning is more convenient but can lead to unexpected costs if PVCs are deleted and recreated. Static provisioning gives more control but requires manual management.
Q: Can I attach a PV to multiple pods across nodes?
A: Only if the PV supports ReadWriteMany access mode. Most cloud provider block storage (EBS, GCE persistent disk) supports ReadWriteOnce only. For multi-attach, use a filesystem like NFS, EFS, or Azure Files. Be aware of performance limitations and potential data corruption if multiple pods write to the same file simultaneously.
Q: How do I back up a PV?
A: Use volume snapshots if your CSI driver supports them. Create a VolumeSnapshot resource, which creates a point-in-time copy of the volume. You can then restore the snapshot to a new PVC. Alternatively, use a backup tool like Velero, which can back up PV data along with cluster resources. Test your backups regularly.
Summary + Next Experiments
The persistent volume illusion is real, but it is not a flaw in Kubernetes—it is a misunderstanding of the PV lifecycle, reclaim policies, and the difference between the PV object and the actual storage. To prevent data loss, follow these steps:
First, audit all existing PVs and PVCs. Check the reclaim policy of each PV. If any critical volume uses Delete, change it to Retain. Second, review your StorageClasses. Ensure that production StorageClasses use Retain or that you have a backup strategy for volumes with Delete. Third, test your stateful setup by simulating pod restarts, node failures, and cluster upgrades. Document the expected behavior and compare it to what actually happens.
Fourth, implement monitoring for PV status. Alert on PVs that are in Released or Failed state, as they may indicate orphaned storage. Fifth, create a backup policy for all stateful workloads. Use volume snapshots or a backup tool like Velero. Test restores at least quarterly. Sixth, educate your team about the PVC lifecycle. Include storage considerations in your deployment guidelines and code reviews.
Finally, consider using a service mesh or a sidecar proxy to handle state migration if you need to move data between clusters. This is an advanced pattern, but it can help with disaster recovery and multi-cluster setups. Start with the basics: understand your reclaim policies, test your persistence, and always have a backup plan. The persistent volume illusion only persists as long as you ignore the details.
Comments (0)
Please sign in to post a comment.
Don't have an account? Create one
No comments yet. Be the first to comment!