DEV Community

Cover image for Understanding Kubernetes Volumes: Persistent Volume and Persistent Volume Claim
Favour Lawrence
Favour Lawrence

Posted on

Understanding Kubernetes Volumes: Persistent Volume and Persistent Volume Claim

Consider a case where data gets added or updated on PostgreSQL, when pod restart all changes will be gone.

Why?

Kubernetes doesn’t automatically persist data when a pod restarts. By default, when a pod dies, its storage disappears with it. This is because Kubernetes treats pods as ephemeral, they come and go, and their associated storage doesn’t stick around unless you explicitly configure it to.

For databases, logs, and any application that needs to retain state, this is a huge problem. That’s where Persistent Volumes (PV) and Persistent Volume Claims (PVC) come in. They allow Kubernetes to handle storage separately from pods, ensuring your data doesn’t vanish every time a pod is replaced.

If you’re coming from Docker, you might wonder how this compares to Docker volumes,
"Doesn’t Docker have volumes for persistent storage?"

Yes, it does. But Kubernetes handles storage in a more decentralized, scalable way. Unlike Docker, where volumes are tied to a single host, Kubernetes volumes are designed to be cluster-wide and can be provisioned dynamically.

In this guide, we’ll break down:
✅ What Persistent Volumes (PV) are and how they’re managed by administrators.
✅ How developers request storage using Persistent Volume Claims (PVC).

By the end, you’ll not only understand how Kubernetes storage works but also be able to set up persistent storage for your own applications. Let’s get started 🚀


How Does Kubernetes Handle Storage?

Kubernetes uses Volumes to provide persistent storage, but here’s the thing:

➡️ A Kubernetes Volume is just an abstraction—it doesn’t store data itself. It needs to be backed by actual physical storage.

So, where does this storage come from?

Types of Storage in Kubernetes

1️⃣ Local Storage (Node-Specific)

  • Tied to a single node.
  • If a pod moves to another node, the data doesn’t follow.
  • Examples:
    • emptyDir (temporary storage that lasts as long as the pod exists).
    • hostPath (uses a directory on the host machine).

2️⃣ Remote Storage (Cluster-Wide)

  • Decoupled from any single node, so pods can move freely without losing data.
  • Provided by external storage systems.
  • Examples:
    • Cloud storage: AWS EBS, Google Persistent Disks, Azure Disk.
    • Network storage: NFS, Ceph, GlusterFS.

Persistent Volumes (PV) and Persistent Volume Claims (PVC)

To make storage management easier, Kubernetes introduces:

✔ Persistent Volumes (PV): The actual storage backend.

✔ Persistent Volume Claims (PVC): A way for pods to request storage dynamically.

Think of it like a hotel:

  • A PV is a hotel room.
  • A PVC is a reservation.
  • When a pod needs storage, it "books" a room (PV) through a PVC.

📌 Kubernetes Volumes are not actual storage—they just connect your pod to a real storage system.


Persistent Volume (PV) – The Admin’s Role

We now know that Kubernetes doesn’t provide storage by itself, so who sets it up?

A PV is a pre-configured storage unit set up by the cluster administrator. Once it's available, developers can claim it using a Persistent Volume Claim (PVC) (which we’ll cover next). But first, let’s see how admins actually set up storage in Kubernetes.

How Do Admins Provision Storage?

Admins set up storage by:

1️⃣ Setting up the storage backend (Local disk, NFS, AWS EBS, etc.).

2️⃣ Defining a Persistent Volume (PV) that connects to this storage.

3️⃣ Making the PV available for developers to claim via PVCs.

Example: Creating a Persistent Volume (PV)

Here’s a simple YAML configuration for a local storage PV:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  hostPath:
    path: "/mnt/data"
Enter fullscreen mode Exit fullscreen mode

Breaking It Down

🔹 capacity.storage: 5Gi → Provides 5GB of storage.

🔹 accessModes: ReadWriteOnce → Can be mounted by only one node at a time.

🔹 persistentVolumeReclaimPolicy: Retain → Storage remains even after the pod using it is deleted.

🔹 storageClassName: local-storage → Specifies which storage class to use.

🔹 hostPath: /mnt/data → Uses a local directory as storage.

Of course, admins can also configure networked storage like NFS, AWS EBS, or Google Persistent Disk instead of using a local directory.

📌 PVs exist independently of pods, ensuring data persists even if a pod is deleted or rescheduled.


Persistent Volume Claim (PVC) – The Developer’s Role

The admin has set up a Persistent Volume (PV)—now how do developers actually use it?

That’s where Persistent Volume Claims (PVCs) come in.

A PVC is a storage request from a developer. It’s like saying:

🗣️ “Hey Kubernetes, I need 5GB of storage with read/write access. Find me a PV that matches!”

If a suitable PV is available, Kubernetes automatically binds the PVC to it, making storage available for the pod.

How Developers Request Storage Using PVC

1️⃣ Create a PVC specifying storage requirements.

2️⃣ Kubernetes finds a matching PV and binds the PVC to it.

3️⃣ Mount the PVC inside a pod to store data persistently.

Example: Creating a PVC

Here’s a simple YAML configuration for a PVC requesting 5GB of storage:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: local-storage
Enter fullscreen mode Exit fullscreen mode

Breaking It Down

🔹 accessModes: ReadWriteOnce → Storage can be mounted by only one node at a time.

🔹 resources.requests.storage: 5Gi → Requests 5GB of storage.

🔹 storageClassName: local-storage → Uses a PV with the matching storage class.

How the PVC Gets Bound to a PV

✅ If a PV with the right storage size, access mode, and storage class exists, Kubernetes automatically binds the PVC to it.

✅ Once bound, the PVC can be used inside a pod for persistent data storage.

Mounting the PVC in a Pod

Now that we have a PVC, let’s use it inside a pod:

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
    - name: my-container
      image: postgres
      volumeMounts:
        - mountPath: "/var/lib/postgresql/data"
          name: my-storage
  volumes:
    - name: my-storage
      persistentVolumeClaim:
        claimName: my-pvc
Enter fullscreen mode Exit fullscreen mode

📌 Now, even if the pod restarts, the PostgreSQL database will still have its data

Thanks for reading! Be sure to follow for more DevOps content, and feel free to comment with the DevOps concepts you'd like to see covered next.

Top comments (2)

Collapse
 
scsoi profile image
疏影 •

The PVC lifecycle on Windows nodes is the gotcha that bit our team twice:

  1. resizeInUse — when you bump a PVC size, kubelet on the Windows node has to remount the volume at the new size. Any process holding an open file handle to the old size (a log scraper, an antivirus) freezes the resize until that process is restarted. We now script a drain + cordon + scale-to-zero before resizing.

  2. AccessModes matter more than the doc suggests — ReadWriteOnce on Windows behaves subtly different than Linux because NTFS reparse points are involved. RBAC drives who can mount, but NTFS ACLs drive what they see. The two layers need to agree.

  3. Storage class defaults — most teams skip the storage class YAML until they need a new tier, then they get bitten by the absence of reclaimPolicy and allowVolumeExpansion. Set both to defaults explicitly so future PVCs inherit sane behavior.

For stateful data we want ops to browse (logs, exports, archives), we run a parallel drive-letter mount of the underlying storage backend. PV lifecycle stays in K8s, ops still gets Explorer access without shelling into the node.

Mount layer: ScsDriver

Collapse
 
scsoi profile image
疏影 •

PV/PVC basics are good but the production gotchas bite hardest:

  1. StorageClass is the most important decision — it defines the provisioner (aws-ebs, gce-pd, azure-disk, ceph-rbd, nfs-subdir) and reclaim policy. Default reclaimPolicy=Delete means deleting the PVC deletes the underlying volume — set Retain for any data you care about, and write a separate cleanup workflow.

  2. WaitForFirstConsumer vs Immediate binding — Immediate binds a PV as soon as you create the PVC, which can fail if no node has the right zone. WaitForFirstConsumer delays binding until the pod schedules, fixing the multi-zone problem. Always use WaitForFirstConsumer for zone-aware cloud disks.

  3. Volume expansion requires allowVolumeExpansion: true in StorageClass AND a CSI driver that supports online resize. AWS EBS gp3 → resize online. ceph-rbd → online if RWO. azure-disk → online. azure-files → online. nfs-subdir → offline only.

  4. RWX (ReadWriteMany) is rare — only NFS, cephfs, azure-files, aws-efs, and the (alpha) aws-ebs-multi-attach support it. RWO (ReadWriteOnce) is the default. Stateful apps with leader-follower (Postgres, MongoDB) need RWO + fencing; do not pretend RWX will fix them.

  5. Snapshot/Restore via CSI — VolumeSnapshot CRD + VolumeSnapshotClass. Pair with Velero for cluster-wide backup. Snapshots are crash-consistent for most CSI drivers; for databases use a quiesce hook first.

For ops on Windows who want to see PV data alongside local disks: ScsDriver