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 Mode | Meaning | Example Use |
|---|---|---|
| ReadWriteOnce (RWO) | One node can read and write | Database volume |
| ReadOnlyMany (ROX) | Many nodes can read, none can write | Shared config files |
| ReadWriteMany (RWX) | Many nodes can read and write | Shared 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
| Policy | What Happens to the PV and Data |
|---|---|
| Retain | PV stays, data preserved — manual cleanup required |
| Delete | PV 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.
