DevOpsPublished April 22, 2026

Kubernetes for DevOps Engineers: Storage, Persistent Volumes, and Stateful Apps Made Simple

Learn Kubernetes storage the simple way: volumes, PersistentVolumes, PersistentVolumeClaims, StorageClasses, dynamic provisioning, and StatefulSets for stateful applications.

Illustrated cover for the article “Kubernetes for DevOps Engineers: Storage, Persistent Volumes, and Stateful Apps Made Simple,” showing a central Kubernetes icon connected to diagrams for PersistentVolume, PersistentVolumeClaim, StorageClass, dynamic provisioning, and StatefulSet on a dark blue cloud infrastructure background.

Kubernetes for DevOps Engineers: Storage, Persistent Volumes, and Stateful Apps Made Simple

Once your workloads are running reliably, the next big question is:

What happens to the data when a Pod restarts, moves, or gets replaced?

This is where Kubernetes storage becomes essential.

Stateless applications are usually easy to replace. Stateful applications are not. If your app needs durable data, Kubernetes storage concepts are no longer optional.

In this guide, we will keep things simple and practical.

The Big Idea

Containers are easy to replace. Data is not.

If a container writes important data only inside its own writable layer, that data is tied to the life of the container.

Kubernetes solves this with volumes and persistent storage objects.

A simple mental model is:

Pod = compute, volume = data

Start With Volumes

A Kubernetes volume is mounted into a Pod and gives containers a place to read or write data.

Some volumes are temporary and live only as long as the Pod. Others are backed by persistent storage that can outlive the Pod.

For production data, the important path is persistent storage.

The 3 Objects You Must Know

  • PersistentVolume (PV): the actual storage resource
  • PersistentVolumeClaim (PVC): the storage request made by your workload
  • StorageClass: the storage tier or provisioning policy

A simple way to remember them:

PV = storage, PVC = request, StorageClass = type of storage

PersistentVolume: The Storage Resource

A PersistentVolume represents storage that exists for workloads to use.

It might come from cloud block storage, network storage, or another storage system supported by the cluster.

You can think of it as the actual piece of storage Kubernetes knows about.

PersistentVolumeClaim: The Request for Storage

Most applications do not ask for a specific PersistentVolume by name.

Instead, they create a PersistentVolumeClaim.

The claim says what the app needs, such as size, access mode, and sometimes a storage class.

Kubernetes then looks for a matching PersistentVolume or provisions one dynamically if the cluster is configured to do that.

Simple PVC Example

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard

How Binding Works

When a PersistentVolumeClaim is created, Kubernetes tries to bind it to a suitable PersistentVolume.

If the cluster has dynamic provisioning and the requested StorageClass supports it, Kubernetes can create the volume automatically.

Once the claim is bound, the Pod can mount that claim and use the storage.

StorageClass: The Storage Tier

A StorageClass describes a class of storage available in the cluster.

Different classes can represent different performance levels, backup behavior, reclaim policies, or provider-specific settings.

In many clusters, one StorageClass is marked as the default.

If your claim does not ask for a specific class, the default may be used.

Simple StorageClass Example

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: example.csi.storage-provider
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer

You do not need to memorize every field on day one. The key idea is that a StorageClass defines how storage should be provisioned.

Dynamic Provisioning: The Modern Default

In older setups, administrators often had to create PersistentVolumes manually before apps could use them.

Modern clusters usually rely on dynamic provisioning instead.

That means a new volume can be created automatically when a matching PersistentVolumeClaim appears.

This is one of the most useful Kubernetes storage features because it removes a lot of manual work.

A Simple Pod Example Using a PVC

apiVersion: v1
kind: Pod
metadata:
name: demo-app
spec:
containers:
- name: app
image: nginx:stable
volumeMounts:
- name: app-data
mountPath: /usr/share/nginx/html
volumes:
- name: app-data
persistentVolumeClaim:
claimName: demo-data

This tells the Pod to mount the storage requested by the demo-data claim.

Access Modes: Who Can Mount the Volume?

A PersistentVolumeClaim includes access modes that describe how the storage can be mounted.

The most common one beginners see is ReadWriteOnce, which usually means the volume can be mounted read-write by a single node at a time.

The exact behavior still depends on the storage system behind it, so it is better to treat access modes as capability hints than as magic guarantees.

Reclaim Policy: What Happens After the Claim Is Deleted?

Storage is not only about creating data. It is also about what happens later.

A PersistentVolume can have a reclaim policy such as Delete or Retain.

This matters a lot in production.

A simple warning is worth remembering:

Deleting a claim should never be a surprise operation.

Stateful Apps Need More Than Storage

Some applications do not just need persistent storage. They also need stable identity.

Databases, brokers, and clustered systems often care which replica is which.

This is where StatefulSets become important.

A StatefulSet gives each Pod a stable identity and is commonly used when each Pod should keep its own storage.

StatefulSet and volumeClaimTemplates

One of the most useful StatefulSet patterns is volumeClaimTemplates.

Instead of manually creating one claim per replica, the StatefulSet can generate a separate PersistentVolumeClaim for each Pod.

This is a clean and common pattern for stateful workloads.

Simple StatefulSet Example

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: demo-db
spec:
serviceName: demo-db
replicas: 2
selector:
matchLabels:
app: demo-db
template:
metadata:
labels:
app: demo-db
spec:
containers:
- name: db
image: nginx:stable
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard

In this pattern, each replica gets its own claim and its own storage.

When to Use a Deployment and When to Use a StatefulSet

Use a Deployment when replicas are interchangeable and the app does not need stable identity.

Use a StatefulSet when each replica needs its own identity, its own storage, or ordered behavior during scaling and updates.

A simple rule:

Stateless app = usually Deployment. Stateful app = usually StatefulSet.

A Practical Troubleshooting Flow

When storage does not work, check the chain step by step.

kubectl get pvc
kubectl get pv
kubectl describe pvc demo-data
kubectl get storageclass
kubectl get statefulset
kubectl describe pod demo-app

The most useful early questions are:

  • Is the claim still Pending?
  • Did it bind to a volume?
  • Is the requested StorageClass available?
  • Is the Pod mounting the right claim?

Common Beginner Mistakes

Confusing a PVC With the Actual Storage

The claim is the request, not the storage itself.

Using Empty Pod Storage for Important Data

Temporary storage is fine for caches and scratch space, but not for critical application data.

Using a Deployment for Stateful Replicas

If each replica needs stable storage or identity, a Deployment is often the wrong fit.

Ignoring the StorageClass

Many binding and provisioning problems come from requesting a class that does not exist or does not behave the way you expect.

Deleting Claims Without Thinking About Data Lifecycle

Storage cleanup behavior depends on the reclaim policy and the storage system behind it.

What a DevOps Engineer Must Remember

  • Containers are replaceable, so important data should live on storage designed to outlive the Pod.
  • PersistentVolume is the storage resource.
  • PersistentVolumeClaim is the workload’s request for storage.
  • StorageClass defines the storage tier or provisioning behavior.
  • Dynamic provisioning is the normal modern pattern.
  • StatefulSets are usually the right choice when Pods need stable identity and stable storage.
  • Storage decisions are operational decisions, not just YAML details.

Final Thought

Kubernetes storage becomes much easier when you stop thinking only about containers and start thinking about data lifecycle.

Ask yourself:

Should this data disappear with the Pod, or should it survive the Pod?

If the data should survive, you are now in the world of PersistentVolumes, claims, storage classes, and often StatefulSets.