📝 Kubernetes

Volumes and Persistent Storage in Kubernetes

P
Author
Pyland
📅
Published
30.06.2026
⏱️
Reading time
3 min
👁️
Views
77
🌳
Level
Advanced

By default, data inside a container disappears along with the container. Kubernetes provides several levels of storage abstraction — from ephemeral to fully persistent.

The Problem: Pod Dies, Data Is Gone

A container’s filesystem is ephemeral. If a Pod restarts — the database is empty, uploaded files are gone, logs have vanished. For stateful applications this is unacceptable.

The solution is Volumes: external storage that outlives a pod.

emptyDir: Temporary Storage

Created with the Pod, destroyed with the Pod. Useful for temporary files and data sharing between containers in the same Pod.

apiVersion: v1
kind: Pod
metadata:
  name: app-with-temp-storage
spec:
  containers:
    - name: app
      image: my-app:1.0
      volumeMounts:
        - name: temp-data
          mountPath: /tmp/cache
    - name: sidecar
      image: busybox
      volumeMounts:
        - name: temp-data
          mountPath: /shared
  volumes:
    - name: temp-data
      emptyDir: {}

Both containers see the same files. Data lives as long as the Pod lives.

hostPath: Mounting a Node Directory

Mounts a directory from the host machine (Worker Node) into the Pod. Data survives pod restarts, but is tied to a specific node.

volumes:
  - name: host-logs
    hostPath:
      path: /var/log/myapp
      type: DirectoryOrCreate

Use cases: accessing host devices, specific dev scenarios. Not recommended in production — the Pod becomes node-dependent.

PersistentVolume and PersistentVolumeClaim

Full-featured persistent storage. Consists of two objects:

  • PersistentVolume (PV) — the actual storage resource. Created by an administrator or automatically (StorageClass). Describes capacity, type, and access parameters.
  • PersistentVolumeClaim (PVC) — a storage request from an application. The developer describes how much space and what type is needed. K8s finds a matching PV.
# persistent-volume.yaml (usually created by an administrator)
apiVersion: v1
kind: PersistentVolume
metadata:
  name: postgres-pv
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce      # single Pod with read/write
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: /data/postgres  # for minikube/dev; in prod use NFS, cloud disk, etc.
# persistent-volume-claim.yaml (created by the developer)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: standard
  resources:
    requests:
      storage: 5Gi
# Using the PVC in a Pod/Deployment
spec:
  containers:
    - name: postgres
      image: postgres:15
      env:
        - name: PGDATA
          value: /var/lib/postgresql/data/pgdata
      volumeMounts:
        - name: postgres-storage
          mountPath: /var/lib/postgresql/data
  volumes:
    - name: postgres-storage
      persistentVolumeClaim:
        claimName: postgres-pvc
kubectl apply -f persistent-volume.yaml
kubectl apply -f persistent-volume-claim.yaml

kubectl get pv
# NAME          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   STORAGECLASS
# postgres-pv   10Gi       RWO            Retain           Bound    standard

kubectl get pvc
# NAME           STATUS   VOLUME        CAPACITY   ACCESS MODES   STORAGECLASS
# postgres-pvc   Bound    postgres-pv   10Gi       RWO            standard

Access Modes

Mode Description
ReadWriteOnce (RWO) Single node, read+write
ReadOnlyMany (ROX) Many nodes, read-only
ReadWriteMany (RWX) Many nodes, read+write (NFS, CephFS)

StorageClass: Dynamic Provisioning

Manually creating a PV for every PVC is impractical. StorageClass automates storage creation.

# storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp3
  iopsPerGB: "10"
  encrypted: "true"
reclaimPolicy: Delete
allowVolumeExpansion: true

Now a PVC with storageClassName: fast-ssd automatically creates an EBS disk in AWS:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 20Gi
# Available StorageClasses in the cluster
kubectl get storageclass
# NAME                 PROVISIONER             AGE
# standard (default)   docker.io/hostpath      10d
# fast-ssd             kubernetes.io/aws-ebs   5d

StatefulSet for Stateful Applications

A regular Deployment is not suitable for databases: pods are interchangeable, have no stable names, and share PVCs.

StatefulSet solves this:

  • Stable pod names: postgres-0, postgres-1, postgres-2
  • Guaranteed startup and shutdown order
  • Each Pod gets its own PVC (VolumeClaimTemplate)
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres-headless
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:15
          env:
            - name: PGDATA
              value: /var/lib/postgresql/data/pgdata
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: [ReadWriteOnce]
        storageClassName: fast-ssd
        resources:
          requests:
            storage: 10Gi

Each of the three pods gets its own PVC: data-postgres-0, data-postgres-1, data-postgres-2. When a pod is deleted, the PVC is preserved — data is not lost.

kubectl get statefulset
# NAME       READY   AGE
# postgres   3/3     5m

kubectl get pvc
# NAME             STATUS   VOLUME          CAPACITY
# data-postgres-0  Bound    pvc-abc...      10Gi
# data-postgres-1  Bound    pvc-def...      10Gi
# data-postgres-2  Bound    pvc-ghi...      10Gi

Your reaction to the article

💬 Comments (0)

🔐 Sign in to leave a comment
🚪 Login
💭

No comments yet

Be the first to share your opinion about this article!

🔗 Similar

Similar articles

Continue learning with these materials

📝

Health Checks: Liveness and Readiness in Kubernet…

A container is running — that does not mean the application is working. There might...

📅 30.06.2026 👁️ 93
📝

Resource Limits and Requests in Kubernetes

Without resource constraints a single "greedy" container can consume all the memory on a node...

📅 30.06.2026 👁️ 83
📝

What Is Kubernetes and Why You Need It

Docker packages an application into a container. But what do you do when you have...

📅 30.06.2026 👁️ 85

Did you like the article?

Subscribe to our updates and receive new articles first. Grow with PyLand!