Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Kubernetes & Helm By James Joyner IV · · 9 min read

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:

  • replicas
  • template (the pod template)
  • updateStrategy
  • persistentVolumeClaimRetentionPolicy
  • minReadySeconds

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 its storageClassName.
  • Changing the selector — adding or modifying matchLabels, which would break the pod-to-claim relationship.
  • Changing podManagementPolicy — switching between OrderedReady and Parallel after creation.
  • Helm chart drift — a chart upgrade that alters an immutable field (often volumeClaimTemplates labels or serviceName) 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, and volumeClaimTemplates as 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 diff in CI before applying StatefulSet changes to catch forbidden edits before they hit the cluster.
  • When a true immutable change is required, script the --cascade=orphan delete-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.

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.

Free download · 368-page PDF

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.