StatefulSets and volumeClaimTemplates

How a StatefulSet gives each Pod its own name, DNS name and claim, and what happens to the claims on scale-down or delete.

30 min 2 hands-on tasksStorage · 10% of the exam

Key ideas in this lesson · tap one for a simple explanation

What you will be able to do

  • Write a StatefulSet with a headless Service and a volumeClaimTemplate, and predict the names of its Pods and claims.
  • Scale a StatefulSet and say which claims are created, kept or deleted under each retention policy.
  • Choose between a Deployment and a StatefulSet for an app and say why.

Some apps run several copies, and each copy needs its own data. A database with three replicas is the classic example. If db-1 restarts, it must get db-1's disk back, not db-0's. A does this. Each gets a fixed name, its own DNS name and its own claim, and keeps all three when it restarts.

This lesson ties the module together. Each Pod's claim comes from a volumeClaimTemplate, and a provisions it. The exam asks you to create a StatefulSet with storage, or to scale one and check its volumes. The grader checks names, so learn the naming rules.

One StatefulSet, one claim per Pod

StatefulSet: blog

2 replicas, claim template: content

Pods

each with its own name, DNS name and claim

blog-0

DNS: blog-0.blog

claim: content-blog-0

blog-1

DNS: blog-1.blog

claim: content-blog-1

PersistentVolumes

one per claim, made by the standard class

Delete blog-1 and it comes back with the same name and the same claim, so it finds its data again. Its full DNS name is blog-1.blog.st-sts.svc.cluster.local.

StatefulSet or Deployment?

DeploymentStatefulSet
Pod namesRandom suffix: web-7d9c8-x4k2pNumbered and fixed: web-0, web-1
Network identityOnly through a Each Pod has its own DNS name, through a headless Service
StorageAll replicas share the volumes in the Pod templateEach replica gets its own claim, and gets it back after a restart
Start and stop orderAll at onceIn order from 0 up; removed from the highest number down
Use it forStateless apps, such as a web front endDatabases, queues, anything where "which copy am I" matters

Stable names: the headless Service

A StatefulSet names a Service in spec.serviceName. That Service is headless (clusterIP: None): it has no virtual IP, and DNS answers with the Pod IPs directly. Each Pod then gets its own DNS name, <pod>.<service>.<namespace>.svc.cluster.local. Example: blog-0.blog.st-sts.svc.cluster.local.

  • Like any Service, its must match the Pod .
  • A client that needs one exact copy, such as the primary database, uses that Pod's name: blog-0.blog.
  • A client that wants any copy uses the Service name, blog.
The headless Service
apiVersion: v1
kind: Service
metadata:
  name: blog
  namespace: st-sts
spec:
  clusterIP: None
  selector:
    app: blog
  ports:
    - name: http
      port: 80
A StatefulSet with two replicas and a claim per Pod
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: blog
  namespace: st-sts
spec:
  serviceName: blog
  replicas: 2
  selector:
    matchLabels:
      app: blog
  template:
    metadata:
      labels:
        app: blog
    spec:
      containers:
        - name: web
          image: nginx:1.27
          ports:
            - containerPort: 80
              name: http
          volumeMounts:
            - name: content
              mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
    - metadata:
        name: content
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: standard
        resources:
          requests:
            storage: 100Mi

Pods start in order

By default (podManagementPolicy: OrderedReady) the Pods start one at a time. blog-0 must be Running and Ready before blog-1 starts. Scaling down removes the highest number first. A database that elects a leader needs this order. With Parallel, all Pods start and stop at once instead.

Either way, a deleted Pod comes back with the same name, the same claim and the same DNS name. That is the whole promise of a StatefulSet.

volumeClaimTemplates and claim names

Each entry in volumeClaimTemplates is a claim spec. For every Pod, the StatefulSet creates one claim from it, named <template>-<statefulset>-<number>. Example: template content on StatefulSet blog gives content-blog-0 and content-blog-1.

  • The 's volumeMounts uses the template name, content, as if it were a volume. There is no volumes: entry for it.
  • The claims are ordinary . The StorageClass provisions them, and kubectl get pvc lists them.
  • Each claim stays tied to the Pod with the same number.
  • You cannot change volumeClaimTemplates on an existing StatefulSet. To change it, delete the StatefulSet and create it again.
Create, inspect and scale
kubectl apply -f blog.yaml
kubectl -n st-sts rollout status sts/blog --timeout=180s
kubectl -n st-sts get pods -l app=blog -o wide          # blog-0, blog-1
kubectl -n st-sts get pvc                                # content-blog-0, content-blog-1, both Bound
kubectl -n st-sts scale sts blog --replicas=3           # creates content-blog-2 then blog-2
kubectl -n st-sts get sts blog -o jsonpath='{.spec.persistentVolumeClaimRetentionPolicy}'

What happens to the claims when you scale down or delete

By default, nothing: the claims stay. Scale from 3 to 2, and content-blog-2 stays Bound with its data. Scale back to 3, and blog-2 gets its old disk back. Delete the StatefulSet, and every claim survives too. That is why old claims pile up after test runs.

Scaling down from 3 to 2

Scale to 2

--replicas=2

blog-2 is removed

the highest number goes first

Claim content-blog-2

kept, unless whenScaled is Delete

spec.persistentVolumeClaimRetentionPolicy changes this. It has two settings, and each one is Retain (the default) or Delete:

SettingApplies whenWith Delete
whenScaledYou scale downThe claim of each removed Pod is deleted once that Pod is gone
whenDeletedYou delete the StatefulSetAll its claims are deleted with it

What happens to the data after a claim is deleted is still up to the PV's reclaim policy, which the StorageClass set. You can add the field to a running StatefulSet with a patch:

Set a retention policy on a running StatefulSet
kubectl -n st-sts patch sts blog -p '{"spec":{"persistentVolumeClaimRetentionPolicy":{"whenScaled":"Delete","whenDeleted":"Retain"}}}'
kubectl -n st-sts scale sts blog --replicas=2
kubectl -n st-sts get pvc                                # content-blog-2 is gone after blog-2 terminates
# Cleaning up by hand when the policy is Retain:
kubectl -n st-sts delete sts blog
kubectl -n st-sts delete pvc -l app=blog                 # claims inherit the selector labels

Updates never touch the claims either. When you change the image, the StatefulSet replaces its Pods one at a time, from the highest number down. Each Pod comes back with its own claim and its data. The docs explain the details under Rolling Updates.

In the official docs· open on exam day too

Hands-on task 1 of 2st-sts-1
~9 min6% weightStorage

A StatefulSet with per-replica storage

In namespace st-sts create a headless Service named blog on port 80 with selector app=blog, and a StatefulSet named blog with 2 replicas of image nginx:1.27 that uses that Service for its network identity. Every replica must get its own 100Mi ReadWriteOnce volume from the StorageClass standard through a volumeClaimTemplate named content, mounted at /usr/share/nginx/html. Both Pods must be Running and both claims Bound.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/st-sts-1/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/st-sts-1/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/st-sts-1/cleanup | bash
Hands-on task 2 of 2st-sts-2
~6 min4% weightStorage

Scale a StatefulSet and control what happens to its claims

The StatefulSet blog in namespace st-sts runs 2 replicas, each with its own volume. Scale it to 3 replicas and make sure the new replica gets its own claim. Also configure the StatefulSet so that from now on the claim of a replica is deleted when the StatefulSet is scaled down, but all claims are kept if the StatefulSet itself is deleted. All 3 Pods must be Running when you finish.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/st-sts-2/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/st-sts-2/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/st-sts-2/cleanup | bash

Finished this lesson?

0 of 2 tasks passed. Your readiness counts tasks, so run them before you move on.