Kubernetes Volumes and Persistent Storage

Containers are stateless by design — when a container stops, all data written inside it disappears. For applications that need to store data permanently, like databases or file upload services, Kubernetes provides Volumes and Persistent Volumes to save data beyond the container's life.

The Stateless Container Problem

Imagine writing notes on a whiteboard in a rental room. When you check out, the staff wipes the whiteboard clean for the next guest. A container's filesystem works the same way. Every restart wipes it. For a database, that would mean losing all your data every time the container restarts.

Container restarts
Before restart: /data/database.db ← your data
After restart:  /data/              ← empty, data is gone

Volumes: Storage Attached to a Pod

A Kubernetes Volume is storage attached to a Pod. Unlike container storage, a Volume persists as long as the Pod lives. If a container inside the Pod restarts, the Volume stays intact. If the entire Pod is deleted, the Volume goes with it — unless you use a Persistent Volume.

emptyDir: Temporary Shared Storage

emptyDir creates an empty directory when the Pod starts. All containers in the Pod share it. It is wiped when the Pod is deleted. Use it for temporary processing — scratch space, caching, or sharing files between a main container and a sidecar.

apiVersion: v1
kind: Pod
metadata:
  name: data-processor
spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  containers:
  - name: producer
    image: my-producer
    volumeMounts:
    - name: shared-data
      mountPath: /output
  - name: consumer
    image: my-consumer
    volumeMounts:
    - name: shared-data
      mountPath: /input

The producer writes files to /output. The consumer reads them from /input. Both paths point to the same emptyDir volume.

Persistent Volumes: Storage Beyond the Pod

A Persistent Volume (PV) is a storage resource in the cluster that exists independently of any Pod. A Persistent Volume Claim (PVC) is a request for a piece of that storage. Think of a PV as a warehouse with available shelf space, and a PVC as a requisition form asking for a specific shelf.

Cluster Admin creates:     Developer requests:
┌────────────────────┐    ┌────────────────────┐
│  Persistent Volume │    │  PV Claim (PVC)    │
│  100 GB SSD        │◄───│  I need 20 GB SSD  │
│  (warehouse shelf) │    │  (requisition form)│
└────────────────────┘    └────────────────────┘
         Kubernetes binds the PVC to the PV

Creating a Persistent Volume

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
spec:
  capacity:
    storage: 50Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: /mnt/data   # For Minikube/local testing only

Access Modes Explained

Access ModeMeaningExample Use
ReadWriteOnce (RWO)One node can read and writeDatabase volume
ReadOnlyMany (ROX)Many nodes can read, none can writeShared config files
ReadWriteMany (RWX)Many nodes can read and writeShared file storage

Creating a Persistent Volume Claim

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: standard

Kubernetes finds a PV that matches the request (size, access mode, storage class) and binds the PVC to it.

Using a PVC in a Pod

spec:
  volumes:
  - name: db-storage
    persistentVolumeClaim:
      claimName: my-pvc
  containers:
  - name: postgres
    image: postgres:15
    volumeMounts:
    - name: db-storage
      mountPath: /var/lib/postgresql/data

The PostgreSQL database writes to /var/lib/postgresql/data. That path is backed by the PVC. Even if this Pod is deleted and recreated, the same PVC (and its data) attaches to the new Pod.

Storage Classes: Dynamic Provisioning

Manually creating PVs for every request does not scale. Storage Classes enable dynamic provisioning — Kubernetes automatically creates a PV when you create a PVC, using a provisioner that talks to your cloud storage API.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/gce-pd   # Google Cloud
parameters:
  type: pd-ssd
reclaimPolicy: Delete
allowVolumeExpansion: true

When a PVC requests the fast-ssd storage class, Google Cloud creates an SSD disk automatically and binds it. AWS uses ebs.csi.aws.com, Azure uses disk.csi.azure.com.

Developer creates PVC with storageClassName: fast-ssd
          ↓
Kubernetes calls the GCE provisioner
          ↓
Google Cloud creates a new SSD disk
          ↓
Kubernetes creates a PV and binds it to the PVC
          ↓
Pod attaches and mounts the volume

Reclaim Policies: What Happens When You Delete a PVC

PolicyWhat Happens to the PV and Data
RetainPV stays, data preserved — manual cleanup required
DeletePV and underlying cloud disk deleted automatically
Recycle (deprecated)Deletes data, makes PV available again (avoid this)

Key Points

  • Container storage is ephemeral — use Volumes to persist data beyond container restarts.
  • emptyDir is shared temporary storage inside a Pod, wiped when the Pod is deleted.
  • Persistent Volumes exist independently of Pods — data survives Pod deletion.
  • A PVC is your request for storage; Kubernetes binds it to a matching PV.
  • Storage Classes enable automatic disk provisioning on cloud platforms with no manual PV creation.

Leave a Comment