Persistent Storage: Volumes, PersistentVolumes, and Claims
Keep data alive across Pod restarts with PVCs, and understand how StorageClasses tie the pieces together.
A container’s filesystem lasts only as long as the container. When a Pod is replaced, anything it wrote is gone. That is fine for stateless web servers and a problem for databases, uploads, and anything else that must survive. This guide shows how Kubernetes attaches durable storage to Pods.
Why container storage isn’t enough
Containers are meant to be small and disposable, so putting data inside them defeats the model. Data has to live outside the container and outside the Pod, on storage that Kubernetes attaches when a Pod starts. Kubernetes is agnostic about what that storage is: a cloud disk, a network share, or a local drive. It also doesn’t back the data up for you. That remains your job.
The storage chain
Four objects work together:
- A StorageClass describes a kind of storage and how to create it on demand (for example “fast SSD volumes from the cloud provider”).
- A PersistentVolume (PV) is an actual piece of storage in the cluster.
- A PersistentVolumeClaim (PVC) is a request for storage by a user or an application: “I need 1 GiB that one node can read and write.”
- The Pod references the claim as a volume and mounts it into a container.
Container ─ mountPath ─▶ Pod volume ─▶ PVC ─ binds to ─▶ PV ─▶ real storage
▲
StorageClass creates PVs on demand
A PVC binds to exactly one PV. The Pod never refers to the PV, only to the claim, which is what keeps applications independent of the storage details.
Request storage with a PVC
Most clusters have a default StorageClass that creates volumes on demand (called dynamic provisioning), and minikube includes one. Check yours:
kubectl get storageclass
Then ask for storage:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
kubectl apply -f pvc.yaml
kubectl get pvc,pv
The claim should become Bound, and a matching PV appears. Some storage classes wait until a Pod actually uses the claim before creating the volume, so Pending at this point isn’t necessarily an error.
Mount it in a Pod and prove persistence
apiVersion: v1
kind: Pod
metadata:
name: storage-demo
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "sleep 3600"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: data
Write a file, destroy the Pod, recreate it, and check that the file survived:
kubectl apply -f pod.yaml
kubectl exec storage-demo -- sh -c 'echo persisted > /data/proof.txt'
kubectl delete pod storage-demo
kubectl apply -f pod.yaml
kubectl exec storage-demo -- cat /data/proof.txt
The new Pod is a completely different container, but the claim, and the data behind it, were reattached.
Access modes, reclaim policy, and static volumes
- Access modes say how a volume can be mounted.
ReadWriteOncemeans read-write by a single node, so several Pods on that node can still share it.ReadOnlyManyandReadWriteManyallow many nodes, if the storage supports it.ReadWriteOncePodrestricts it to a single Pod. - Reclaim policy decides what happens to the volume when the claim is deleted. Dynamically provisioned volumes usually default to
Delete, which removes the underlying storage and its data. UseRetainwhen you want to keep the data. - Static provisioning means an administrator creates PVs by hand and claims bind to whichever one fits. A
hostPathPV, which uses a directory on a node, is a common demo, but it ties data to one machine and is only suitable for single-node development.
Databases and StatefulSets
You can mount one claim into a Deployment’s Pods, but replicas of a Deployment are interchangeable and identical, and most databases don’t work that way. A StatefulSet is built for stateful workloads. It gives each Pod a stable identity (db-0, db-1), starts and updates them in order, and can create a separate PVC for each replica:
spec:
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
A StatefulSet handles identity and storage, not data replication. Making replicas consistent with each other is the database’s job, which is why running databases on Kubernetes is harder than running stateless services. Many teams use a managed database or a database operator, and keep the stateless applications in the cluster.
Popular Guides
-
What Kubernetes Is and the Problems It Solves Why containers alone stop scaling, and how Kubernetes turns a group of machines into one platform. -
How a Kubernetes Cluster Works: Control Plane and Nodes The components that make a cluster tick, and what happens between kubectl apply and a running container. -
Deployments: Scaling, Self-Healing, and Rolling Updates Run replicas of a Pod, update them without downtime, and roll back when a release goes wrong. -
Services: Stable Networking for Ephemeral Pods Give Pods a stable address, expose them inside and outside the cluster, and restrict who can talk to whom. -
Debug a Failing Pod: describe, logs, exec, and port-forward A repeatable checklist for finding out why a Pod is Pending, crashing, or not answering.