Home/Learn/Kubernetes/Persistent Storage — PV, PVC & StorageClass

Persistent Storage — PV, PVC & StorageClass

Intermediate
Storage

Kubernetes PersistentVolumes (PV) and PersistentVolumeClaims (PVC) decouple storage provisioning from consumption. StorageClasses enable dynamic provisioning of cloud volumes (EBS, GCE PD, Azure Disk).

Overview

Pods are ephemeral — their filesystems vanish when the pod dies. For stateful workloads (databases, file storage), you need Persistent Volumes. The K8s storage model has three layers: PersistentVolume (PV) — a piece of actual storage (AWS EBS, NFS share, local disk) provisioned by an admin or dynamically by a StorageClass. PersistentVolumeClaim (PVC) — a pod's request for storage of a certain size and access mode. StorageClass — defines how PVs are dynamically provisioned (which cloud provider, IOPS, type). Dynamic provisioning is the standard on cloud — you create a PVC and K8s automatically provisions the cloud volume.

PersistentVolumeClaim — Requesting Storage

In cloud environments, you usually just create a PVC and let the StorageClass dynamically provision the underlying volume. You never have to manually create the PV.

pvc.yaml — PersistentVolumeClaim
# pvc.yaml — request 20 GB of SSD storage

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: postgres-data

  namespace: production

spec:

  accessModes:

    - ReadWriteOnce          # RWO: mounted read-write by ONE node at a time

    # ReadWriteMany          # RWX: mounted by multiple nodes (NFS, EFS)

    # ReadOnlyMany           # ROX: mounted read-only by multiple nodes

  storageClassName: gp3      # which StorageClass to use (cloud-specific)

  resources:

    requests:

      storage: 20Gi          # request 20 GB



# K8s automatically provisions an AWS EBS gp3 volume and binds it to this PVC



# Use PVC in a Deployment

spec:

  template:

    spec:

      containers:

        - name: postgres

          image: postgres:16-alpine

          volumeMounts:

            - name: data

              mountPath: /var/lib/postgresql/data

      volumes:

        - name: data

          persistentVolumeClaim:

            claimName: postgres-data    # references the PVC above



# Check PVC status

kubectl get pvc

# NAME           STATUS  VOLUME        CAPACITY  ACCESS MODES  STORAGECLASS

# postgres-data  Bound   pvc-abc123    20Gi      RWO           gp3

StorageClass — Dynamic Provisioning

StorageClasses define how storage is provisioned. Cloud providers ship default StorageClasses. You can create custom ones with specific disk types, IOPS, or reclaim policies.

StorageClass — dynamic provisioning
# List available StorageClasses

kubectl get storageclass

# NAME              PROVISIONER             RECLAIMPOLICY  VOLUMEBINDINGMODE

# gp2 (default)     kubernetes.io/aws-ebs   Delete         WaitForFirstConsumer

# gp3               ebs.csi.aws.com         Delete         WaitForFirstConsumer

# standard          kubernetes.io/gce-pd    Delete         Immediate



# Custom StorageClass — AWS EBS io2 (high IOPS for databases)

apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

  name: io2-database

provisioner: ebs.csi.aws.com

parameters:

  type: io2

  iopsPerGB: "50"           # 50 IOPS per GB → 20GB PVC = 1000 IOPS

  encrypted: "true"

reclaimPolicy: Retain       # Retain: keep volume after PVC deletion

volumeBindingMode: WaitForFirstConsumer  # provision only when pod is scheduled



# reclaimPolicy:

# Delete  → volume deleted when PVC is deleted (default) — ⚠️ data loss!

# Retain  → volume kept after PVC deletion, must be manually reclaimed

# Recycle → deprecated



# Expand a PVC (must be supported by StorageClass)

kubectl patch pvc postgres-data -p '{"spec":{"resources":{"requests":{"storage":"50Gi"}}}}'

# ⚠️ EBS volumes require pod restart to see expanded size on some versions

StatefulSet — For Stateful Applications

Deployments are for stateless apps. StatefulSets are for databases and clustered apps — they provide stable pod names, stable network identities, and ordered startup/shutdown.

StatefulSet — for databases and clustered apps
# StatefulSet provides:

# 1. Stable pod names: pod-0, pod-1, pod-2 (not random like Deployment)

# 2. Stable DNS: pod-0.my-service.default.svc.cluster.local

# 3. Ordered startup: pod-0 ready → pod-1 starts → pod-2 starts

# 4. Ordered shutdown: pod-2 terminates → pod-1 → pod-0

# 5. VolumeClaimTemplates: each pod gets its OWN PVC



apiVersion: apps/v1

kind: StatefulSet

metadata:

  name: postgres

spec:

  serviceName: postgres          # headless service name for DNS

  replicas: 3

  selector:

    matchLabels:

      app: postgres

  template:

    metadata:

      labels:

        app: postgres

    spec:

      containers:

        - name: postgres

          image: postgres:16-alpine

          env:

            - name: POSTGRES_PASSWORD

              valueFrom:

                secretKeyRef:

                  name: postgres-secret

                  key: password

          volumeMounts:

            - name: data

              mountPath: /var/lib/postgresql/data

  volumeClaimTemplates:          # each pod gets its own PVC!

    - metadata:

        name: data

      spec:

        accessModes: [ReadWriteOnce]

        storageClassName: gp3

        resources:

          requests:

            storage: 20Gi

# Creates: data-postgres-0, data-postgres-1, data-postgres-2

# Each pod has its own 20 GB volume — not shared

Key Points to Remember

  • 1PVC is the request for storage; PV is the actual storage; StorageClass handles dynamic provisioning.
  • 2ReadWriteOnce (RWO): one node at a time — for databases. ReadWriteMany (RWX): multiple nodes — for shared file storage.
  • 3reclaimPolicy: Delete destroys the volume when PVC is deleted — use Retain for production databases.
  • 4StatefulSets give pods stable names (pod-0, pod-1) and each pod its own PVC via volumeClaimTemplates.
  • 5Deployments = stateless; StatefulSets = stateful (databases, caches with persistence).
  • 6Dynamic provisioning on cloud means you only create a PVC — K8s creates the EBS/GCE/Azure disk automatically.

Interview Questions

Sign in to ask Aria
1

What is the difference between a Deployment and a StatefulSet?

2

What happens to the PVC when a StatefulSet pod is deleted and rescheduled?

Ask Aria about Persistent Storage — PV, PVC & StorageClass

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…