Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

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

Sponsored

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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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 pv to 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 ReadWriteMany PVC 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.fsGroup in 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 persistentVolumeClaim in 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 emptyDir persists 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 ReadWriteMany when the underlying volume only supports ReadWriteOnce will fail.
  • Not setting fsGroup for non-root containers, causing permission denied errors when writing to the mounted volume.

Variations

  1. Use ReadWriteMany PVCs (e.g., NFS) when multiple pods need to share the same persistent data simultaneously.
  2. Leverage a StorageClass with custom parameters — such as ssd or hdd — for performance-cost tradeoffs in dynamic provisioning.
  3. 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 pvc and kubectl describe pv to diagnose binding issues and stuck claims.
  • Always pair PVCs with security context (fsGroup) when containers run as non-root to avoid permission errors.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.