skip to content

An application's configuration lives in a Kubernetes ConfigMap that is marked `immutable: true`. Walk through how you ship a configuration change to the running Pods.

level: middleimportance: should knowfreq 30%

answer

  1. new name, not new content
  2. hash suffix → template change → automatic rollout
  3. rollout restart is for the mutable case
  4. undo works because the old ConfigMap still exists
  5. prune, but keep N versions

basics

~20 s

Create a new ConfigMap under a new name (usually a content-hash or version suffix), update the Pod template to reference that name, and apply. The template change triggers a rolling update by itself, so Pods pick up the new config. Then prune superseded ConfigMaps.

solid answer

~50 s

You never edit it — you publish a new one. The pattern is: 1. Render the new config into a ConfigMap whose **name carries a version or content hash**, e.g. `app-config-9d41b2`, also marked immutable. 2. Change the Deployment's Pod template to reference the new name (in `volumes.configMap.name` or `envFrom.configMapRef.name`). 3. `kubectl apply`. Because the Pod template changed, the Deployment controller creates a new ReplicaSet and does a normal rolling update — you get progress tracking, `kubectl rollout status`, and `kubectl rollout undo` for rollback. 4. Garbage-collect old ConfigMaps once no ReplicaSet references them. Kustomize's `configMapGenerator` (with `disableNameSuffixHash: false`, the default) does steps 1–2 automatically by appending a content hash and rewriting references; Helm users usually template the name or add a checksum annotation. `kubectl rollout restart` is *not* the mechanism here — that is what you need in the mutable case, where the name stays the same and nothing in the template changes.

code

yaml · 22 lines
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: checkout
spec:
  replicas: 3
  selector:
    matchLabels: { app: checkout }
  template:
    metadata:
      labels: { app: checkout }
    spec:
      containers:
        - name: app
          image: registry.example.com/checkout:1.8.3
          volumeMounts:
            - name: config
              mountPath: /etc/checkout
      volumes:
        - name: config
          configMap:
            name: app-config-9d41b2   # bump this to ship a config change

go deeper

for a junior

Say the core recipe: new ConfigMap with a new name, point the Deployment at it, apply — the Pods roll because the template changed.

for a middle

Add the hash-suffix naming and the Kustomize configMapGenerator implementation, and explain why rollout restart belongs to the mutable workflow instead.

for a senior

Emphasise that the change is now a versioned, revertible release, and cover the pruning/retention policy that keeps rollback possible without letting objects accumulate.

for a principal

Frame it as the platform's config-delivery contract: generation and naming owned by tooling, retention policy, ownership/GC model, and how it interacts with the GitOps controller's prune behaviour.

## Why the usual workflow breaks With a mutable ConfigMap the common workflow is: edit the object, then `kubectl rollout restart deployment/app` so Pods re-read it. That works because the Deployment's Pod template still points at the same name; the restart just recycles Pods so environment variables and freshly-mounted files reflect the new content. Immutability removes step one — the edit is rejected — so the whole workflow has to be replaced rather than patched. ## The versioned-name pattern The replacement is to treat config objects like container image tags: the name identifies a specific content, and new content means a new name. ``` app-config-7f2c1a # LOG_LEVEL=info app-config-9d41b2 # LOG_LEVEL=debug ``` The suffix can be a hash of the contents (preferred — identical content produces an identical name, so re-applying is a no-op) or an incrementing version (simpler for humans, but two people can collide). Either way the Deployment's Pod template names the exact object it wants. The important consequence is that **the rollout falls out for free**. A Deployment rolls whenever its `spec.template` changes; changing `volumes[].configMap.name` from `app-config-7f2c1a` to `app-config-9d41b2` is a template change. The controller creates a new ReplicaSet, scales it up under your `maxSurge`/`maxUnavailable` settings, and scales the old one down. You get everything you would want from a code deploy: `kubectl rollout status` to watch, `kubectl rollout pause` to hold, `kubectl rollout undo` to revert — and the revert genuinely works, because the old ConfigMap still exists under its old name and the old ReplicaSet still references it. This is the argument that sells immutability to sceptics: it is not extra ceremony, it is the config change becoming a first-class, revertible release. ## Where `kubectl rollout restart` fits Candidates often reach for `kubectl rollout restart` here. It is the right tool in the *mutable* world: the object was edited in place, the template is byte-identical, and something must force Pods to restart — `rollout restart` stamps `kubectl.kubernetes.io/restartedAt` into the template annotation to trigger a roll. In the immutable world the template already changed, so the restart is redundant. Mentioning it as "the tool for when the reference name did not change" shows you understand the mechanism rather than reciting a command. ## Tooling that does it for you - **Kustomize `configMapGenerator` / `secretGenerator`**: generates the object, appends a hash of the content to the name, and rewrites every reference in the overlay. Add `options: {immutable: true}` (supported in modern Kustomize) and the generated objects carry the flag. This is the canonical implementation of the pattern. - **Helm**: either template the resource name with a value/version, or — because Helm has no built-in hash-naming — use the older `checksum/config` annotation trick on the Pod template, which forces a roll when content changes. Note that the annotation trick alone still leaves you editing the object in place, so with immutability you want name templating. - **GitOps controllers** apply whatever the repo says; the pattern is compatible, but pruning behaviour matters (see below). ## Cleaning up Every change leaves a superseded object behind. Left unmanaged, a busy service accumulates hundreds of ConfigMaps and you lose the etcd/API-server savings that motivated immutability in the first place. Options: - Let the deployment tool prune: Kustomize/`kubectl apply --prune` with a label selector, or Argo CD's prune, deletes generated objects that are no longer referenced by the desired state. - Owner references: make the ConfigMap owned by something with a lifecycle (a Deployment or a custom resource) so Kubernetes garbage collection removes it when the owner goes. - A retention policy: keep the last N versions per app so `rollout undo` still has something to point at. Deleting the previous ConfigMap immediately makes rollback fail — the old ReplicaSet's Pods will not start because their volume source no longer exists. That last point is the subtle operational trap: prune too eagerly and you have made rollback impossible at exactly the moment you need it. ## The emergency case If you truly must change the value under the same name — say a broken pipeline and a live incident — the only route is `kubectl delete` followed by recreate (`kubectl replace --force` does this in one step). During the gap the object does not exist, so any Pod that starts in that window fails to mount it and lands in `CreateContainerConfigError`. Treat it as a break-glass action, not a workflow.

  • Why is `kubectl rollout restart` not what you use in the immutable case?
    `rollout restart` exists to force a roll when the Pod template has not changed — it stamps a `restartedAt` annotation into the template. With versioned names the template already changed because the ConfigMap reference is different, so the Deployment controller rolls on its own. Adding a restart is harmless but redundant, and reaching for it first usually signals you are still thinking in edit-in-place terms.
  • What breaks rollback in this pattern?
    Deleting the previous ConfigMap too eagerly. `kubectl rollout undo` restores the old ReplicaSet, whose Pods reference the old ConfigMap name; if that object was pruned, the Pods fail to mount and sit in `CreateContainerConfigError`. Keep the last few versions, or scope pruning to objects no live ReplicaSet references.

saying these in an interview costs you the question

  • Saying you would `kubectl edit` the ConfigMap and then restart the Deployment — the edit is rejected outright.
  • Using `kubectl replace --force` as the normal workflow, ignoring that it deletes the object and breaks Pods starting in the gap.
  • Forgetting that changing the referenced name is itself what triggers the rolling update.
  • Pruning every superseded ConfigMap immediately and then being surprised that `rollout undo` fails.

context