Skip to main content

Storage

Your container writes a file. The container crashes. Kubernetes restarts it. The file is gone.

Containers are ephemeral by design — their filesystem resets on every restart. For anything that needs to survive beyond a container's lifetime, you need a volume.

Kubernetes has two tiers:

emptyDir — temporary storage that lives as long as the pod. Shared between containers in the same pod. Gone when the pod is deleted. Use it for caching, scratch space, or passing data between containers in a sidecar pattern.

PersistentVolume (PV) + PersistentVolumeClaim (PVC) — storage that lives independently of any pod. The pod claims it; the data persists even if the pod is replaced, rescheduled, or deleted. Use it for databases, user uploads, anything stateful.


How PV and PVC work

A PersistentVolume is a piece of storage provisioned by an admin (or dynamically by a StorageClass). It exists cluster-wide.

A PersistentVolumeClaim is a request from a pod for a piece of that storage. It says: "I need 1Gi, read-write." Kubernetes finds a PV that satisfies the claim and binds them together.

The pod mounts the PVC. The pod doesn't know or care where the actual storage lives — whether it's a local disk, NFS, or an EBS volume.


Hands-on

Lab files

Fork eigenbytes-devops-labs — manifests for this lab are in 04-kubernetes/storage/: emptydir-demo.yaml, pvc-demo.yaml, and pvc-reader.yaml.

emptyDir — shared scratch space

Save as emptydir-demo.yaml:

apiVersion: v1
kind: Pod
metadata:
name: emptydir-demo
spec:
containers:
- name: writer
image: busybox
command: ["sh", "-c", "echo 'hello from writer' > /shared/data.txt; sleep 3600"]
volumeMounts:
- name: shared
mountPath: /shared
- name: reader
image: busybox
command: ["sh", "-c", "sleep 2; cat /shared/data.txt; sleep 3600"]
volumeMounts:
- name: shared
mountPath: /shared
volumes:
- name: shared
emptyDir: {}
kubectl apply -f emptydir-demo.yaml
kubectl logs emptydir-demo -c reader
# hello from writer

Two containers in one pod, sharing a directory. Neither container has a filesystem dependency on the other — they communicate through the shared volume.

PersistentVolumeClaim — data that survives pod deletion

Save as pvc-demo.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: pvc-writer
spec:
containers:
- name: app
image: busybox
command: ["sh", "-c", "echo 'persistent data' > /data/output.txt; sleep 3600"]
volumeMounts:
- name: storage
mountPath: /data
volumes:
- name: storage
persistentVolumeClaim:
claimName: demo-pvc
restartPolicy: Never
kubectl apply -f pvc-demo.yaml
kubectl get pvc
NAME       STATUS   VOLUME     CAPACITY   ACCESS MODES   STORAGECLASS
demo-pvc Bound pvc-xyz 1Gi RWO hostpath

Now delete the pod and create a new one reading from the same PVC:

kubectl delete pod pvc-writer

Save as pvc-reader.yaml:

apiVersion: v1
kind: Pod
metadata:
name: pvc-reader
spec:
containers:
- name: app
image: busybox
command: ["sh", "-c", "cat /data/output.txt"]
volumeMounts:
- name: storage
mountPath: /data
volumes:
- name: storage
persistentVolumeClaim:
claimName: demo-pvc
restartPolicy: Never
kubectl apply -f pvc-reader.yaml
kubectl logs pvc-reader
# persistent data

The pod is gone. The data is not.


Cleanup

kubectl delete pod emptydir-demo pvc-writer pvc-reader --ignore-not-found
kubectl delete pvc demo-pvc --ignore-not-found
rm emptydir-demo.yaml pvc-demo.yaml pvc-reader.yaml

Quick reference

kubectl get pv                            # list PersistentVolumes
kubectl get pvc # list PersistentVolumeClaims
kubectl describe pvc <name> # check binding status
kubectl get storageclass # list available storage classes
kubectl delete pvc <name> # release storage claim

Lab files: eigenbytes-devops-labs/04-kubernetes/storage