Skip to main content

Storage in Kubernetes:

 Storage in Kubernetes: Persistent Volumes (PV), Persistent Volume Claims (PVC), and Storage Classes

📅 Published: May 2026
⏱️ Estimated Reading Time: 18 minutes
🏷️ Tags: Kubernetes, Persistent Volumes, PVC, Storage Classes, Stateful Applications


Introduction: The Storage Problem in Kubernetes

Containers are ephemeral. When a container restarts, anything written to its filesystem disappears. For stateless applications, this is fine. But databases, message queues, and file storage applications need their data to survive beyond the life of a single container.

Kubernetes solves this problem with Persistent Volumes (PV) and Persistent Volume Claims (PVC). These abstractions separate the provision of storage from its consumption, making it easier for application developers to request storage without understanding the underlying infrastructure.

Think of it like a library. The library (Storage Class) owns many books (Persistent Volumes). You fill out a request form (Persistent Volume Claim) asking for a specific type of book. The librarian finds a matching book and gives it to you. You don't need to know where the book came from or how the library manages its inventory.

This guide explains how storage works in Kubernetes, covering Persistent Volumes, Persistent Volume Claims, and Storage Classes.


Part 1: The Storage Abstractions

The Three Key Concepts

ConceptWhat It IsAnalogy
Persistent Volume (PV)Storage resource provisioned by an administratorA physical hard drive
Persistent Volume Claim (PVC)Request for storage by a userA requisition form for a hard drive
Storage ClassDefinition of different storage tiersA catalog of drive types (SSD, HDD)
text
┌─────────────────────────────────────────────────────────────────┐
│                    Cluster Administrator                         │
│                         │                                        │
│                         ▼                                        │
│              ┌─────────────────────┐                            │
│              │   Storage Class     │                            │
│              │  (ssd, standard)    │                            │
│              └──────────┬──────────┘                            │
│                         │                                        │
│                         ▼                                        │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐              │
│  │     PV      │  │     PV      │  │     PV      │              │
│  │ (100Gi SSD) │  │ (50Gi HDD)  │  │ (200Gi SSD) │              │
│  └─────────────┘  └─────────────┘  └─────────────┘              │
│                         │                                        │
│                         │ (bound)                                │
│                         ▼                                        │
│              ┌─────────────────────┐                            │
│              │        PVC          │                            │
│              │  Request: 50Gi SSD  │                            │
│              └──────────┬──────────┘                            │
│                         │                                        │
│                         ▼                                        │
│                    ┌─────────┐                                  │
│                    │   Pod   │                                  │
│                    └─────────┘                                  │
│                         │                                        │
│                   Application Developer                          │
└─────────────────────────────────────────────────────────────────┘

Part 2: Persistent Volumes (PV)

What is a Persistent Volume?

A Persistent Volume is a piece of storage in the cluster provisioned by an administrator. It is a cluster resource, just like CPU and memory. PVs are independent of any individual Pod and exist at the cluster level.

PV YAML Example

yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: manual-pv
  labels:
    type: local
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  local:
    path: /mnt/data
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - worker-node-1

PV Spec Fields Explained

FieldPurposeCommon Values
capacityHow much storagestorage: 100Gi
volumeModeRaw block or filesystemFilesystem, Block
accessModesHow the volume can be mountedSee table below
persistentVolumeReclaimPolicyWhat happens after PVC is deletedRetain, Delete, Recycle
storageClassNameWhich Storage Class this PV belongs tostandard, ssd
mountOptionsFilesystem mount optionshard, nfsvers=4.1

Access Modes

ModeDescriptionUse Case
ReadWriteOnce (RWO)Read and write by a single nodeDatabase, single Pod
ReadOnlyMany (ROX)Read-only by many nodesConfig files, shared data
ReadWriteMany (RWX)Read and write by many nodesShared filesystems, web uploads
ReadWriteOncePod (RWOP)Read and write by a single Pod (K8s 1.27+)Exclusive access

PV Reclaim Policies

PolicyWhat HappensWhen to Use
RetainPV remains with its dataProduction data, needs manual cleanup
DeletePV and underlying storage are deletedEphemeral storage, testing
Recyclerm -rf /volume/* (deprecated)Legacy workloads

Static Provisioning

In static provisioning, the administrator creates PVs manually:

bash
# Create a PV backed by local storage
kubectl apply -f local-pv.yaml

# List PVs
kubectl get pv

# Delete a PV
kubectl delete pv manual-pv

Part 3: Persistent Volume Claims (PVC)

What is a Persistent Volume Claim?

A PVC is a request for storage by a user or application. It specifies the amount of storage needed, access mode, and optionally the Storage Class. When a PVC is created, Kubernetes binds it to a matching PV.

PVC YAML Example

yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 5Gi
  storageClassName: standard

PVC Spec Fields

FieldPurpose
accessModesMust match PV's access modes
resources.requests.storageHow much storage you need
storageClassNameWhich Storage Class to use
selectorMatch specific PV labels
volumeNameBind to a specific PV

Using PVC in a Pod

yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: app
    image: nginx
    volumeMounts:
    - name: storage
      mountPath: /data
  volumes:
  - name: storage
    persistentVolumeClaim:
      claimName: my-pvc

PVC Lifecycle

text
1. PVC Created → Pending (waiting for PV)
2. PV Found → Bound (PVC bound to PV)
3. Pod Uses PVC → Mounted (Pod can read/write)
4. PVC Deleted → Released (PV becomes Released)
5. PV Reclaim Policy → Available, Retained, or Deleted

PVC Operations

bash
# Create PVC
kubectl apply -f pvc.yaml

# List PVCs
kubectl get pvc
kubectl get pvc -n my-namespace

# Describe PVC
kubectl describe pvc my-pvc

# Delete PVC
kubectl delete pvc my-pvc

# Check binding status
kubectl get pvc,pv

Part 4: Storage Classes

What is a Storage Class?

A Storage Class defines different tiers of storage. It allows administrators to offer different quality-of-service levels: faster SSD storage for databases, slower HDD storage for backups, or region-specific storage for compliance.

Storage Classes enable dynamic provisioning: PVCs can request storage without pre-provisioned PVs.

Storage Class YAML Example

yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp3
  fsType: ext4
  iops: "3000"
  throughput: "125"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer

Storage Class Fields

FieldPurpose
provisionerWhich storage plugin to use
parametersProvisioner-specific settings
reclaimPolicyWhat happens when PVC is deleted
allowVolumeExpansionWhether PVC size can be increased
volumeBindingModeWhen to bind PV to PVC

Common Provisioners

CloudProvisionerExample
AWSebs.csi.aws.comgp2, gp3 volumes
GCPpd.csi.storage.gke.iopd-ssd, pd-standard
Azuredisk.csi.azure.commanaged-premium, managed-standard
Localkubernetes.io/no-provisionerStatic provisioning only
NFSnfs.csi.k8s.ioShared filesystem

AWS EBS Storage Class Example

yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: aws-gp3
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  fsType: ext4
  iops: "3000"
  throughput: "125"
  encrypted: "true"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer

GCP Persistent Disk Example

yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gcp-ssd
provisioner: pd.csi.storage.gke.io
parameters:
  type: pd-ssd
  replication-type: none
  fsType: ext4
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer

Azure Disk Example

yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: azure-premium
provisioner: disk.csi.azure.com
parameters:
  skuname: Premium_LRS
  cachingmode: ReadOnly
  kind: managed
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer

Part 5: Dynamic Provisioning

What is Dynamic Provisioning?

Dynamic provisioning automatically creates PVs when PVCs are created. Instead of administrators pre-creating PVs, the system creates them on demand using a Storage Class.

How Dynamic Provisioning Works

text
1. User creates PVC with Storage Class
2. Provisioner detects the PVC
3. Provisioner creates storage in cloud (EBS, GCE PD, Azure Disk)
4. Kubernetes creates PV representing that storage
5. PVC binds to the new PV
6. Pod uses the PVC
7. When PVC is deleted, PV may be deleted (depends on reclaimPolicy)

Default Storage Class

Set a default Storage Class so PVCs without a class use it:

yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2

Now PVCs without storageClassName automatically use the standard class.


Part 6: Volume Binding Modes

ModeDescriptionUse Case
ImmediatePV is created and bound immediatelyStandard workloads
WaitForFirstConsumerPV created when Pod is scheduledLocal storage, topology-aware

WaitForFirstConsumer Example

WaitForFirstConsumer delays PV creation until a Pod uses the PVC. This ensures the PV is created in the same availability zone as the Pod.

yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
volumeBindingMode: WaitForFirstConsumer
parameters:
  type: gp3

Part 7: StatefulSets and Storage

StatefulSets for Stateful Applications

StatefulSets are designed for stateful applications where each Pod needs its own persistent storage. They provide stable network identities and ordered deployment.

yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: fast-ssd
      resources:
        requests:
          storage: 100Gi

How VolumeClaimTemplates Work

  • Each replica gets its own PVC

  • PVCs are named data-mysql-0, data-mysql-1, data-mysql-2

  • Pods retain their PVC even if rescheduled

text
StatefulSet: mysql
│
├── Pod: mysql-0 → PVC: data-mysql-0 → PV: pv-001
├── Pod: mysql-1 → PVC: data-mysql-1 → PV: pv-002
└── Pod: mysql-2 → PVC: data-mysql-2 → PV: pv-003

Real-World Scenarios

Scenario 1: Database with Persistent Storage

A PostgreSQL database needs 100GB of fast, reliable storage that survives Pod restarts.

yaml
# Storage Class for fast storage
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  iops: "3000"
---
# PVC requesting 100GB from fast-ssd
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 100Gi
---
# Deployment using the PVC
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:15
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: postgres-pvc

Scenario 2: Shared Filesystem for Web Uploads

A web application allows users to upload files. Multiple Pods need read-write access to the same directory.

yaml
# Using NFS or ReadWriteMany storage
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: uploads-pvc
spec:
  accessModes:
    - ReadWriteMany           # Multiple Pods can write
  storageClassName: nfs-csi
  resources:
    requests:
      storage: 500Gi
---
# Deployment with multiple replicas sharing the PVC
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: app
        image: mywebapp:latest
        volumeMounts:
        - name: uploads
          mountPath: /app/uploads
      volumes:
      - name: uploads
        persistentVolumeClaim:
          claimName: uploads-pvc

Scenario 3: Resizing a PVC

A database is running out of space. You need to increase its storage.

yaml
# First, expand the PVC
# Edit the PVC and change storage request
kubectl edit pvc postgres-pvc
# Change: resources.requests.storage: 200Gi

# Check that expansion is allowed
kubectl get storageclass fast-ssd -o yaml | grep allowVolumeExpansion
# should show: allowVolumeExpansion: true

# Check expansion progress
kubectl get pvc postgres-pvc
# Status: FilesystemResizePending → Resizing

Scenario 4: Clone a PVC (K8s 1.16+)

Create a new PVC from an existing one (for backups or testing).

yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: cloned-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-ssd
  dataSource:
    name: postgres-pvc
    kind: PersistentVolumeClaim
  resources:
    requests:
      storage: 100Gi

Storage Operations Commands

bash
# List Storage Classes
kubectl get storageclass
kubectl get sc

# Describe a Storage Class
kubectl describe sc fast-ssd

# Set default Storage Class
kubectl patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

# List Persistent Volumes
kubectl get pv
kubectl get pv -o wide

# List Persistent Volume Claims
kubectl get pvc
kubectl get pvc -A

# Check binding status
kubectl get pvc,pv

# Describe PVC
kubectl describe pvc my-pvc

# Delete PVC (also deletes PV if reclaimPolicy is Delete)
kubectl delete pvc my-pvc

Summary

ConceptPurposeWho Creates
Storage ClassDefines storage tiersAdministrator
Persistent Volume (PV)Actual storage resourceAdministrator or provisioner
Persistent Volume Claim (PVC)Request for storageApplication developer
Static ProvisioningAdministrator creates PVAdministrator
Dynamic ProvisioningPV created automaticallyStorage Class provisioner

Best Practices

  • Use Storage Classes: Always define Storage Classes for different performance tiers

  • Set reclaimPolicy to Retain for production: Prevent accidental data loss

  • Use WaitForFirstConsumer: For topology-aware storage (EBS, GCE PD)

  • Set resource requests: Always specify storage size

  • Use StatefulSets for databases: They handle PVCs properly

  • Monitor storage usage: Set up alerts for low disk space

  • Regular backups: PVs are not backed up by Kubernetes


Practice Questions

  1. What is the difference between a Persistent Volume and a Persistent Volume Claim?

  2. When would you use ReadWriteMany access mode?

  3. What is dynamic provisioning and why is it useful?

  4. How does WaitForFirstConsumer volume binding mode work?

  5. What is the difference between StatefulSet and Deployment for storage?


Learn More

Practice Kubernetes storage with hands-on exercises in our interactive labs:
https://devops.trainwithsky.com/

Comments

Popular posts from this blog

📊 Monitoring & Logging in Kubernetes – Tools like Prometheus, Grafana, and Fluentd

  Monitoring & Logging in Kubernetes – Tools like Prometheus, Grafana, and Fluentd Monitoring and logging are essential for maintaining a healthy and well-performing Kubernetes cluster. In this guide, we’ll cover why monitoring is important, key monitoring tools like Prometheus and Grafana, and logging tools like Fluentd to help you gain visibility into your cluster’s performance and logs. Shape Your Future with AI & Infinite Knowledge...!! Want to Generate Text-to-Voice, Images & Videos? http://www.ai.skyinfinitetech.com Read In-Depth Tech & Self-Improvement Blogs http://www.skyinfinitetech.com Watch Life-Changing Videos on YouTube https://www.youtube.com/@SkyInfinite-Learning Transform Your Skills, Business & Productivity – Join Us Today! 🚀 Introduction In today’s fast-paced cloud-native environment, Kubernetes has emerged as the de-facto container orchestration platform. But deploying and managing applications in Kubernetes is just half the ba...

How to Use SKY TTS: The Complete, Step-by-Step Guide for 2025

 What is SKY TTS? SKY TTS  is a free, next-generation  AI audio creation platform  that brings together high-quality  Text-to-Speech ,  Speech-to-Text , and a full suite of professional  audio editing tools  in one seamless experience. Our vision is simple — to make advanced audio technology  free, accessible, and effortless  for everyone. From creators and educators to podcasters, developers, and businesses, SKY TTS helps users produce  studio-grade voice content  without expensive software or technical skills. With support for  70+ languages, natural voices, audio enhancement, waveform generation, and batch automation , SKY TTS has become a trusted all-in-one toolkit for modern digital audio workflows. Why Choose SKY TTS? Instant Conversion:  Enjoy rapid text-to-speech generation, even with large documents. Advanced Voice Settings:   Adjust speed, pitch, and style for a personalized listening experience. Multi-...

Introduction to Terraform – The Future of Infrastructure as Code

  Introduction to Terraform – The Future of Infrastructure as Code In today’s fast-paced DevOps world, managing infrastructure manually is outdated . This is where Terraform comes in—a powerful Infrastructure as Code (IaC) tool that allows you to define, provision, and manage cloud infrastructure efficiently . Whether you're working with AWS, Azure, Google Cloud, or on-premises servers , Terraform provides a declarative, automation-first approach to infrastructure deployment. Shape Your Future with AI & Infinite Knowledge...!! Read In-Depth Tech & Self-Improvement Blogs http://www.skyinfinitetech.com Watch Life-Changing Videos on YouTube https://www.youtube.com/@SkyInfinite-Learning Transform Your Skills, Business & Productivity – Join Us Today! In today’s digital-first world, agility and automation are no longer optional—they’re essential. Companies across the globe are rapidly shifting their operations to the cloud to keep up with the pace of innovatio...