Kubernetes Administration — Level by Level›07 · Storage basics

Lesson 07 of 32 · Level 1 — Foundations

Storage basics

Containers forget everything when they restart. Learn the volume types, how PersistentVolumeClaims and StorageClasses give pods durable disks, and prove your data survives a pod being deleted.

Beginner → Practitioner
Key wordsvolumeemptyDirPVCPVStorageClassaccess modesreclaim policy
Pod volumeMounts: /var/lib/data PVC 10Gi, RWO storageClass: fast PV bound to the PVC reclaimPolicy StorageClass fast → provisioner Real storage cloud disk, Ceph, local NVMe… CSI driver creates & attaches uses binds asks for
Pod → PVC → PV → real storage, with the StorageClass and CSI driver doing the provisioning.

Containers forget

A container's filesystem is built fresh from the image every time it starts. Write a file, restart the container, and the file is gone. That's great for predictability and terrible for databases.

A container's disk is a whiteboard that gets wiped clean every morning. If you want to keep your homework, you write it in a notebook and put it in your locker. Kubernetes volumes are the notebooks and lockers:

  • emptyDir is a notebook you share with your deskmate for the day. It's thrown away when the class ends (the pod is deleted).
  • A PersistentVolumeClaim is your locker. You can leave, come back tomorrow (a new pod), and your stuff is still there.

The three pieces of persistent storage

Piece Who creates it What it is
StorageClass Cluster admin A type of storage: "fast SSD", "cheap HDD", "shared NFS".
PersistentVolumeClaim (PVC) You (the app) A request: "I need 1 GiB, read-write".
PersistentVolume (PV) Created automatically by the StorageClass's provisioner The actual disk that satisfies the claim.

You ask for storage with a PVC; Kubernetes finds or dynamically creates a PV that matches, and binds them together.

$ kubectl get storageclass
NAME                 PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      AGE
standard (default)   rancher.io/local-path   Delete          WaitForFirstConsumer   1h

On kind, the default standard class creates folders on the node's disk. On EKS it would create EBS volumes; on OpenStack, Cinder volumes; on bare metal, Longhorn or Ceph. Your PVC YAML stays the same, and that's the point.

Prove data survives a pod

Create a claim:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: notes
spec:
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 1Gi
  # storageClassName omitted → the default class is used
$ kubectl apply -f pvc.yaml
persistentvolumeclaim/notes created
$ kubectl get pvc notes
NAME    STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
notes   Pending                                      standard       4s

Pending is normal here. The class waits for a pod to use the claim (WaitForFirstConsumer) so the disk is created on the right node. Now a pod that writes to it:

apiVersion: v1
kind: Pod
metadata:
  name: writer
spec:
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "date >> /data/log.txt; cat /data/log.txt; sleep 3600"]
      volumeMounts:
        - name: notes
          mountPath: /data
  volumes:
    - name: notes
      persistentVolumeClaim:
        claimName: notes
$ kubectl apply -f writer.yaml
$ kubectl get pvc notes
NAME    STATUS   VOLUME                                     CAPACITY   ACCESS MODES
notes   Bound    pvc-3f1c2a4e-9d8b-4c1e-a7f2-0b6e5d4c3b2a   1Gi        RWO
$ kubectl logs writer
Sat Sep 26 18:02:11 UTC 2026
$ kubectl delete pod writer
$ kubectl apply -f writer.yaml
$ kubectl logs writer
Sat Sep 26 18:02:11 UTC 2026
Sat Sep 26 18:04:37 UTC 2026

Two lines: the first pod's write survived the pod being deleted. 🎉

emptyDir: shared scratch space

emptyDir starts empty when the pod starts and is deleted with the pod. It survives container restarts, and it's how two containers in the same pod share files:

spec:
  containers:
    - name: producer
      image: busybox:1.36
      command: ["sh", "-c", "while true; do date > /shared/now.txt; sleep 5; done"]
      volumeMounts: [{ name: shared, mountPath: /shared }]
    - name: consumer
      image: busybox:1.36
      command: ["sh", "-c", "while true; do cat /shared/now.txt; sleep 5; done"]
      volumeMounts: [{ name: shared, mountPath: /shared }]
  volumes:
    - name: shared
      emptyDir: {}

Be careful with hostPath

hostPath mounts a directory from the node itself. It ties the pod to one node and can expose the node's filesystem (a security risk). It's used by system agents (log collectors, CNI), not by normal apps.

Try it: find your data on the node

  1. Run kubectl get pv and kubectl describe pv <name>. Look for the node affinity and the path on the node.
  2. kubectl get pod writer -o wide shows which node it's on. Open a shell there: docker exec -it lab-worker bash (or lab-worker2).
  3. ls /var/local-path-provisioner/, then cat the log.txt inside. That's the "locker", physically.
  4. Delete the PVC (kubectl delete pod writer; kubectl delete pvc notes) and check the folder again. With reclaimPolicy: Delete, it's gone.

Going deeper: what bites in production

  • RWO is per node, not per pod. Two pods on the same node can mount the same RWO volume. Need strict single-writer? Use ReadWriteOncePod.
  • Zones: cloud block disks (EBS) live in one availability zone, and a pod can't mount a disk from another zone. That's why WaitForFirstConsumer matters.
  • Node failure with RWO: the replacement pod may sit in ContainerCreating with a Multi-Attach error until the old attachment is released. Know how your CSI driver handles this before you need it.
  • Use Retain for data you can't afford to lose, and back up with VolumeSnapshots and Velero. A reclaim policy is not a backup.
  • For databases, use a StatefulSet with volumeClaimTemplates: each replica gets its own PVC (data-db-0, data-db-1). More in Kubernetes Storage & Data Protection.

Recap

  • Container filesystems are ephemeral; use volumes for anything worth keeping.
  • emptyDir = pod-lifetime scratch space; PVC = durable storage that outlives pods.
  • StorageClass (type) → PVC (request) → PV (actual disk), provisioned dynamically.
  • Pending with WaitForFirstConsumer is normal; RWO means one node; reclaim policy Delete deletes your data.

This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.