Kubernetes Error Guide: 'updates to statefulset spec ... are forbidden'
Fix the forbidden StatefulSet update error: serviceName, selector, and volumeClaimTemplates are immutable — only replicas, template, and updateStrategy change.
- #kubernetes-helm
- #troubleshooting
- #errors
- #statefulset
Exact Error Message
You edit a StatefulSet — change its serviceName, tweak volumeClaimTemplates, or adjust the selector — and the API server rejects the update outright:
$ kubectl apply -f redis-sts.yaml
The StatefulSet "redis" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden
The same rejection appears from kubectl edit or a Helm upgrade. The message enumerates the only mutable fields. Anything you changed outside that list — most commonly serviceName, selector, or volumeClaimTemplates — triggers the Forbidden validation error.
What the Error Means
StatefulSets give pods a stable identity: a stable network name (via the headless serviceName), stable storage (via volumeClaimTemplates), and a stable label selector tying pods to claims. Those guarantees only hold if the fields defining the identity never change after creation. So the API server marks most of spec immutable and permits in-place updates to just a few fields:
replicastemplate(the pod template)updateStrategypersistentVolumeClaimRetentionPolicyminReadySeconds
Any change to serviceName, selector, podManagementPolicy, or volumeClaimTemplates is rejected before it is persisted. This is a validation (admission) error, not a runtime failure — nothing in the cluster changed. To apply such a change you must delete and recreate the StatefulSet, usually with --cascade=orphan to preserve the pods and PVCs.
Common Causes
- Editing
serviceName— renaming the headless Service the StatefulSet points at. - Changing
volumeClaimTemplates— resizing storage inline, renaming a template, adding/removing a volume, or changing itsstorageClassName. - Changing the
selector— adding or modifyingmatchLabels, which would break the pod-to-claim relationship. - Changing
podManagementPolicy— switching betweenOrderedReadyandParallelafter creation. - Helm chart drift — a chart upgrade that alters an immutable field (often
volumeClaimTemplateslabels orserviceName) on an existing release. - Mismatched template labels vs selector — editing pod template labels so they no longer match the immutable selector.
How to Reproduce the Error
Create a StatefulSet, then try to change its serviceName:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis # change this on a second apply
replicas: 2
selector:
matchLabels: { app: redis }
template:
metadata:
labels: { app: redis }
spec:
containers:
- name: redis
image: registry.k8s.io/pause:3.9
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
kubectl apply -f redis-sts.yaml
# edit serviceName to redis-headless, then:
kubectl apply -f redis-sts.yaml
The StatefulSet "redis" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden
Diagnostic Commands
# See the current immutable field values
kubectl get statefulset <STS> -o jsonpath='{.spec.serviceName}{"\n"}'
kubectl get statefulset <STS> -o jsonpath='{.spec.selector}{"\n"}'
kubectl get statefulset <STS> -o jsonpath='{.spec.volumeClaimTemplates[*].metadata.name}{"\n"}'
# Diff your manifest against the live object to find the changed field
kubectl diff -f <MANIFEST>.yaml
# Confirm which pods and PVCs exist before any recreate
kubectl get pods -l app=<APP>
kubectl get pvc -l app=<APP>
kubectl diff is the fastest way to see exactly which immutable field your manifest changed.
Step-by-Step Resolution
1. Identify the changed immutable field. Run kubectl diff and compare your manifest to the live spec. The offending field is one of serviceName, selector, volumeClaimTemplates, or podManagementPolicy.
kubectl diff -f redis-sts.yaml
2. Decide whether you actually need the change. Inline PVC resizing is often unnecessary: you can usually expand storage by editing the PVCs directly (if the StorageClass allows allowVolumeExpansion) without touching the immutable template:
kubectl get storageclass <SC> -o jsonpath='{.allowVolumeExpansion}'
3. To change an immutable field, recreate the StatefulSet without deleting pods. Delete the StatefulSet object with --cascade=orphan so its pods and PVCs survive, then apply the updated manifest:
kubectl delete statefulset <STS> --cascade=orphan
kubectl apply -f <updated-manifest>.yaml
The new StatefulSet adopts the existing pods (which already match the selector/labels) and reuses the existing PVCs by their deterministic names.
4. If the selector itself changed, expect to recreate pods. A new selector cannot adopt pods with old labels. Plan a maintenance window: orphan-delete, relabel pods or accept recreation, then apply. PVCs persist as long as their names are unchanged.
5. Verify identity and data are intact. Confirm the pods are re-adopted and PVCs still bound:
kubectl get pods -l app=<APP> -o wide
kubectl get pvc -l app=<APP>
6. Fix the source manifest / chart. Update your Helm values or manifest so the change is permanent and future applies are clean, avoiding repeated drift.
Prevention and Best Practices
- Treat
serviceName,selector,podManagementPolicy, andvolumeClaimTemplatesas set-once values — design them carefully before first apply. - Resize StatefulSet storage by editing the PVCs (with
allowVolumeExpansion), not by editing the immutable template. - Run
kubectl diffin CI before applying StatefulSet changes to catch forbidden edits before they hit the cluster. - When a true immutable change is required, script the
--cascade=orphandelete-and-reapply and rehearse it in staging. - Pin Helm chart versions and review StatefulSet diffs on upgrade, since chart changes frequently touch immutable fields. More in Kubernetes & Helm guides.
Related Errors
- StatefulSet has not progressed — a mutable
templatechange that stalls mid-rollout. - StatefulSet persistentvolumeclaim not found — what can go wrong when recreating a StatefulSet.
- PersistentVolumeClaim not bound — storage binding issues after a recreate.
Frequently Asked Questions
Why are these fields immutable at all? They define each pod’s stable identity — network name, storage, and label binding. Allowing them to change in place would silently break the pod-to-PVC and pod-to-DNS guarantees that StatefulSets exist to provide.
Can I resize a StatefulSet’s volumes without recreating it? Yes, if the StorageClass has allowVolumeExpansion: true. Edit each PVC’s spec.resources.requests.storage directly. You cannot edit the volumeClaimTemplates on the live StatefulSet, but the PVCs themselves are expandable.
Will --cascade=orphan delete my data? No. It deletes only the StatefulSet object, leaving pods and PVCs running. Reapplying the manifest re-adopts them. The PVCs keep their deterministic names and bound PVs, so data is preserved.
My change was only to the pod template — why was it rejected? It almost certainly altered the template’s labels so they no longer match the immutable selector, which counts as a forbidden selector mismatch. Keep template labels consistent with the original selector.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.