PVs & PVCs
PersistentVolumes (PVs) and PersistentVolumeClaims (PVCs) are the core Kubernetes resources for managing persistent storage. PVs represent actual storage resources in the cluster, while PVCs are requests for storage by users or applications. This separation allows developers to request storage without knowing implementation details, while administrators maintain control over storage resources.
The PV/PVC Model
The PersistentVolume/PersistentVolumeClaim model separates what storage is needed (declared in a PVC) from how that storage is provided (implemented in a PV). This abstraction makes storage management more flexible and user-friendly.
PersistentVolume (PV)
A PersistentVolume is a cluster resource that represents a piece of storage that has been provisioned. It’s a cluster-wide resource, not namespaced, and exists independently of any pod.
Key characteristics:
- Cluster-scoped resource
- Created by administrators or dynamically by StorageClasses
- Contains storage details (size, access mode, storage type)
- Can be bound to one PVC at a time
PersistentVolumeClaim (PVC)
A PersistentVolumeClaim is a request for storage by a user. It’s similar to how a pod requests CPU and memory—the pod declares its needs, and Kubernetes fulfills them.
Key characteristics:
- Namespaced resource
- Created by developers/users
- Declares storage requirements (size, access mode, StorageClass)
- Gets bound to a matching PV
The Binding Process
Here’s how a PVC gets bound to a PV:
Static Provisioning
With static provisioning, administrators manually create PVs, and users create PVCs that bind to these pre-created PVs.
Example PV (manually created):
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-manual
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: /mnt/data
Example PVC (binds to the PV):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-manual
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: manual
Dynamic Provisioning
With dynamic provisioning, users create PVCs that reference a StorageClass, and Kubernetes automatically creates the PV.
Example PVC (triggers dynamic provisioning):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-dynamic
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd # References StorageClass
resources:
requests:
storage: 50Gi
Kubernetes automatically:
- Looks up the
fast-ssdStorageClass - Uses the provisioner to create storage
- Creates a PV for the new storage
- Binds the PVC to the PV
Using PVCs in Pods
Once a PVC is bound, you can use it in a pod:
apiVersion: v1
kind: Pod
metadata:
name: app-with-pvc
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: data
mountPath: /var/www/html
volumes:
- name: data
persistentVolumeClaim:
claimName: pvc-dynamic # Reference the PVC
Complete Example
Here’s a complete example showing dynamic provisioning:
# StorageClass (usually created by cluster admin)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard
reclaimPolicy: Delete
---
# PVC (created by developer)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: web-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: standard
resources:
requests:
storage: 10Gi
---
# Pod using the PVC
apiVersion: v1
kind: Pod
metadata:
name: web-server
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: web-storage
mountPath: /usr/share/nginx/html
volumes:
- name: web-storage
persistentVolumeClaim:
claimName: web-data
PV States
A PV goes through several states during its lifecycle:
- Available - PV exists but is not bound to a PVC
- Bound - PV is bound to a PVC
- Released - PVC was deleted, but PV hasn’t been reclaimed yet
- Failed - Volume failed to reclaim
PVC States
A PVC also has states:
- Pending - PVC created but not yet bound to a PV
- Bound - PVC is bound to a PV and ready to use
- Lost - The bound PV was deleted (rare)
Best Practices
- Use dynamic provisioning - Prefer StorageClasses over static provisioning
- Set appropriate sizes - Request enough storage but not excessive amounts
- Match access modes to needs - Use ReadWriteOnce unless you need multi-pod access
- Understand reclaim policies - Know what happens to data when PVCs are deleted
- Use StatefulSets for ordered deployments - StatefulSets provide ordered, stable pod identity
- Monitor PVC status - Check if PVCs are bound before deploying pods
- Plan for expansion - Use StorageClasses with
allowVolumeExpansion: truewhen possible
Common Patterns
Database with Persistent Storage
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
Shared Content Storage
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shared-content
spec:
accessModes:
- ReadWriteMany # Multiple pods can mount
storageClassName: nfs-storage
resources:
requests:
storage: 500Gi
Troubleshooting
PVC Stuck in Pending
If a PVC is stuck in Pending state:
- Check if StorageClass exists:
kubectl get storageclass - Verify provisioner is available
- Check for sufficient storage resources
- Review PVC events:
kubectl describe pvc <name>
PV Stuck in Released
If a PV is stuck in Released state after PVC deletion:
- Check the reclaim policy (Retain vs Delete)
- For Retain policy, manually delete the PV to clean up
- Verify the underlying storage can be deleted (if using Delete policy)
Topics
- Patterns - Common PV and PVC usage patterns
See Also
- StorageClasses - Dynamic provisioning with StorageClasses
- Access Modes & Reclaim Policy - PV access modes and reclaim policies
- StatefulSets - Managing stateful applications with persistent storage