Attach a PersistentVolumeClaim
Learn to attach a PersistentVolumeClaim to a Kubernetes pod with hands-on steps, troubleshooting, and practical examples.
Focus: attach a persistentvolumeclaim to a pod
You've built a pod, and maybe even deployed it across nodes. But when that pod restarts or gets rescheduled, what happens to its data? It vanishes. Containers are ephemeral by design, and that's a problem for any application that needs to persist state — databases, file uploads, logs, or configuration caches.
That's where PersistentVolumeClaims (PVCs) come in. They abstract away the messy details of how storage is provisioned and ask Kubernetes a simple question: "Can I have a piece of durable storage, please?" This lesson shows you how to attach a PersistentVolumeClaim to a pod so your data survives restarts, crashes, and pod rescheduling.
The problem this lesson solves
When a pod crashes or is updated, all data written to its filesystem disappears. You can't rely on a container's writable layer for anything important. Early in your Kubernetes journey, you might have used emptyDir volumes for temporary scratch space — but those are tied to the pod's lifecycle and get wiped when the pod is gone.
Consider these real-world pain points:
- A MySQL pod restarts, and you lose your entire database.
- A log aggregator pod moves to a new node, and your historic logs vanish.
- A file-upload service reboots, and users lose their uploads.
Attaching a PersistentVolumeClaim solves all this. It gives your pod a stable identity to durable storage — a PersistentVolume that survives no matter how many times the pod comes and goes.
Core concept / mental model
Think of a PersistentVolume (PV) as a chunk of storage in your cluster — a network-attached disk, an NFS share, or a cloud provider's EBS volume. It's the resource.
A PersistentVolumeClaim (PVC) is a request for storage. You don't need to know exactly which PV backs your claim; you just specify size and access mode (e.g., ReadWriteOnce). Kubernetes finds or dynamically provisions a PV that matches the claim.
Finally, you attach the PVC to a pod by referencing the PVC name in the pod's volume spec. This is the glue that connects an ephemeral pod to durable storage.
Pro tip: Think of the PV/PVC relationship as analogous to a server rack (PV) and a requisition form (PVC). The form says "I need 10GB of fast disk" — Kubernetes then finds a rack that fits that request and binds them together.
How it works step by step
-
A PV is created — either manually by an admin or automatically via a StorageClass (dynamic provisioning). The PV has a real capacity and an access mode.
-
A PVC is created — you specify desired storage size, access mode, and sometimes a storage class. Kubernetes looks for an existing PV that matches. If found, it binds the claim to the volume.
-
The pod references the PVC — inside your pod spec, you declare a volume that points to the PVC name. Then you mount that volume into a container path.
-
Pod is scheduled — the kubelet attaches the actual storage device to the node, mounts it inside the container, and your application sees the files from the PV.
-
Data survives — even if the pod restarts or is recreated, the PVC/PV binding persists. The new pod attaches the same storage.
Hands-on walkthrough
Let's create a complete example: a PVC bound to a dynamically provisioned PV, then attached to an nginx pod that writes a simple test file.
Step 1: Create a PVC (this triggers a PV)
First, make sure your cluster has a default StorageClass (most managed Kubernetes clusters do — verify with kubectl get storageclass).
# pvc-example.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Apply it:
kubectl apply -f pvc-example.yaml
Check the status:
kubectl get pvc
# Output:
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
# my-pvc Bound pvc-3b1f8d9c-7a2e-4b5c-9d1f-6e8a2c7b4d3f 1Gi RWO standard 10s
Step 2: Attach the PVC to a pod
Now create a pod that uses my-pvc:
# pod-with-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
name: pvc-demo-pod
spec:
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: my-pvc
containers:
- name: nginx
image: nginx:alpine
volumeMounts:
- name: data-volume
mountPath: /data
Apply the pod:
kubectl apply -f pod-with-pvc.yaml
Step 3: Write and read data
Get inside the pod and create a file:
kubectl exec -it pvc-demo-pod -- /bin/sh
cd /data
echo "Hello from PVC!" > test.txt
cat test.txt
# Output:
# Hello from PVC!
exit
Now delete and recreate the pod to prove persistence:
kubectl delete pod pvc-demo-pod
kubectl apply -f pod-with-pvc.yaml
kubectl exec -it pvc-demo-pod -- cat /data/test.txt
# Output:
# Hello from PVC!
The file survives! The new pod attached the same PVC, which was already bound to the underlying PV containing your data.
Compare options / when to choose what
Not all volumes are the same. Here's how PVCs compare to other volume types:
| Volume Type | Lifecycle | Persistence | Use Case |
|---|---|---|---|
emptyDir |
Pod lifetime | No | Scratch space, cache, shared temp files |
hostPath |
Node lifetime | Yes (on that node) | Single-node testing, node-level data (not portable) |
| PVC (with PV) | PV lifetime | Yes (across pods/nodes) | Databases, stateful apps, logs, file storage |
configMap / secret |
API object lifetime | Ephemeral (not for large data) | Configuration, credentials |
When to use PVCs:
- Your data must outlive the pod.
- You need data portability across nodes.
- Your application is stateful (databases, message queues, key-value stores).
- You want dynamic provisioning (StorageClass) to simplify ops.
When NOT to use PVCs:
- Temporary scratch space — use
emptyDir. - Config files — use ConfigMaps.
- Sensitive data under 1MB — use Secrets.
Troubleshooting & edge cases
PVC stuck in Pending
kubectl describe pvc my-pvc
Look for:
no persistent volumes available for this claim and no storage class is set— you need a StorageClass or pre-provisioned PV.Failed to provision volume with StorageClass "standard": ...— cloud provider quota or permissions issue.
Fix:
- If using dynamic provisioning, ensure a default StorageClass exists:
kubectl get storageclass. - Manually create a PV that matches the PVC request.
Volume is mounted but empty after pod restart
Check:
- Did the PVC bound to the same PV? Use
kubectl get pvto verify. - Did the process inside the container write to the correct mount path? Look for typos in
mountPath.
AccessModes conflict
PVC requests ReadWriteOnce but two pods try to mount the same PVC. Only one pod can use a RWO volume at a time unless the volume supports multi-attach (e.g., NFS with ReadWriteMany).
Fix:
- Use
ReadWriteManyPVC if supported by your storage class. - Use a per-pod PVC (stateful application pattern).
Permissions issues inside mounted volume
The PV might have restrictive permissions. If your container runs as a non-root user, the mounted filesystem may not allow writes.
Fix:
- Adjust
securityContext.fsGroupin the pod spec to match the group ID of the mounted storage.
securityContext:
fsGroup: 1000
What you learned & what's next
You now know how to:
- Create a PersistentVolumeClaim and understand the PV-PVC binding.
- Attach a PVC to a pod via
persistentVolumeClaimin the volumes section. - Mount the claim inside a container using
volumeMounts. - Persist data across pod restarts.
This is the foundation of stateful workloads in Kubernetes. The next lesson builds on this by introducing StatefulSets — the workload API designed for applications that need stable network identities and ordered scaling alongside persistent storage.
Master the PVC now, and StatefulSets will feel like a natural progression.
Practice recap
Try modifying the hands-on example: create a PVC with a different size (e.g., 500Mi), attach it to a pod running a PostgreSQL container, and verify the database survives a kubectl delete pod followed by a recreation. Then, explore kubectl get pv to see how the PV was dynamically created.
Common mistakes
- Assuming
emptyDirpersists across pod restarts — it does not; always use PVCs for durable data. - Forgetting to create the PVC before referencing it in a pod spec — the pod will fail to schedule if the PVC is missing.
- Mismatching accessModes: a pod requesting
ReadWriteManywhen the underlying volume only supportsReadWriteOncewill fail. - Not setting
fsGroupfor non-root containers, causing permission denied errors when writing to the mounted volume.
Variations
- Use
ReadWriteManyPVCs (e.g., NFS) when multiple pods need to share the same persistent data simultaneously. - Leverage a
StorageClasswith custom parameters — such asssdorhdd— for performance-cost tradeoffs in dynamic provisioning. - For legacy or on-prem clusters, create PVs manually (static provisioning) and let PVCs bind to them without a StorageClass.
Real-world use cases
- Database pods (MySQL, PostgreSQL) using PVCs to preserve transaction logs and data across restarts and node failures.
- File upload services storing user-generated content on a shared NFS-backed PVC accessible to multiple read replicas.
- Log aggregation pipelines writing to a PVC so historical logs survive pod termination and can be processed by batch jobs later.
Key takeaways
- A PVC is a request for durable storage; Kubernetes binds it to a matching PV automatically.
- Attach a PVC to a pod by referencing the claim name in
volumes.persistentVolumeClaim.claimName. - Data written to a mounted PVC survives pod restarts and rescheduling because the PV persists independently.
- AccessModes (RWO, RWX, ROX) control whether one or many pods can mount the volume concurrently.
- Use
kubectl describe pvcandkubectl describe pvto diagnose binding issues and stuck claims. - Always pair PVCs with security context (
fsGroup) when containers run as non-root to avoid permission errors.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.