Skip to main content

Command Palette

Search for a command to run...

Deployment vs StatefulSet for PVCs in Kubernetes: Which One Should You Use (and When)?

Updated
4 min read
A

DevOps engineer & developer passionate about building scalable, reliable systems. I design and automate pipelines, manage cloud infrastructure, and ensure deployments run smoothly. Turning complex workflows into seamless operations is my craft.

"One PVC to rule them all? Or one PVC per pod? The answer depends on how stateful your app really is."

The Tale of Two Pods: Nginx and MySQL

Once upon a time in the land of Kubernetes, there lived two pods — Nginx and MySQL.

  • Nginx was a stateless web server, fast and carefree. He didn’t worry about losing data — if he crashed, he’d just spin back up like nothing happened.

  • MySQL, on the other hand, was very stateful. She guarded her treasure — gigabytes of important data — and couldn’t afford to lose a single bit.

One day, both wanted a place to store their data, so they asked the Kubernetes King for help.

The king gave Nginx a simple Deployment with a shared PVC.
But for MySQL, he created a StatefulSet, with a personal storage room (PVC) for each pod, and a name that never changedmysql-0, mysql-1.

And thus, each pod lived happily ever after:

  • Nginx, serving pages with speed,

  • MySQL, safely storing data without fear of losing identity.

And Kubernetes? It orchestrated their lives with ease.

The end.


Why PVCs Matter in Kubernetes

Kubernetes pods are ephemeral — they come and go. But your data should not.

This is where PVCs (PersistentVolumeClaims) come in. They let your pods store data outside themselves so it can survive restarts.

But how you attach that PVC depends on whether you're using a:

  • Deployment

  • StatefulSet

And that changes everything.


Quick Comparison: Deployment vs StatefulSet (for PVCs)

FeatureDeploymentStatefulSet
Pod namesRandom (nginx-66cbf)Stable (nginx-0, nginx-1)
PVCsShared PVC for all podsUnique PVC per replica
Ordered scaling❌ No✅ Yes
Use caseWeb apps, frontend serversDBs, Kafka, Elasticsearch
Dynamic PVC creation❌ Not supported✅ via volumeClaimTemplates

Let’s See Them in Action!


Scenario 1: Deployment with PVC (Shared Storage)

You want 2 NGINX replicas sharing HTML files from a volume.

Step 1: Create the PVC

# shared-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: shared-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: standard

Step 2: Deploy with Shared PVC

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          volumeMounts:
            - name: shared-volume
              mountPath: /usr/share/nginx/html
      volumes:
        - name: shared-volume
          persistentVolumeClaim:
            claimName: shared-pvc

Warning: Only one pod can mount a ReadWriteOnce volume at a time. The second pod may fail.

If you use ReadOnlyMany or ReadWriteMany (like NFS or GlusterFS), both pods can mount it.


Scenario 2: StatefulSet with PVC (Per-Pod Storage)

You’re deploying MySQL or Redis — each pod needs its own storage.

Step 1: Create a Headless Service

# headless-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
    - port: 3306

Step 2: StatefulSet with volumeClaimTemplates

# mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: "mysql"
  replicas: 2
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:5.7
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: "mypassword"
          volumeMounts:
            - name: mysql-storage
              mountPath: /var/lib/mysql
  volumeClaimTemplates:
    - metadata:
        name: mysql-storage
      spec:
        accessModes: [ "ReadWriteOnce" ]
        storageClassName: "standard"
        resources:
          requests:
            storage: 1Gi

This automatically creates:

  • mysql-storage-mysql-0 for pod mysql-0

  • mysql-storage-mysql-1 for pod mysql-1


Test It

To check the PVCs created:

kubectl get pvc

For StatefulSet:

mysql-storage-mysql-0   Bound
mysql-storage-mysql-1   Bound

For Deployment (only one shared):

shared-pvc              Bound

Summary: Deployment vs StatefulSet for PVCs

CriteriaDeploymentStatefulSet
Shared PVC✅ Easy❌ Not ideal
Unique PVC per pod❌ Not possible✅ Auto-generated
Scale safely❌ Risk of volume mount errors✅ Safe scaling
DNS & identity❌ Random✅ Stable
Production DBs❌ No✅ Yes

Conclusion

Kubernetes makes storage powerful and flexible, but knowing when to use Deployment vs StatefulSet for PVCs is key:

  • Use Deployment when:

    • Your pods can share data

    • You don’t need persistent identity

  • Use StatefulSet when:

    • You need one volume per replica

    • You're running DBs, caches, or distributed systems

More from this blog

Stack OverFlowed

14 posts