skip to content

You edit a Kubernetes ConfigMap that a running Deployment already consumes. Which of its consumers pick up the new values without a Pod restart, which never do, and roughly how long does propagation take?

level: middleimportance: must knowfreq 64%

answer

  1. volume yes, env never, subPath never
  2. kubelet ..data symlink swap = atomic
  3. ~1 minute, not instant
  4. propagation ≠ reload
  5. checksum annotation forces the rollout

basics

~20 s

Files from a configMap volume are refreshed by the kubelet, typically within about a minute. Environment variables never update, and a volume mounted with subPath never updates. Even a refreshed file only helps if the application re-reads it.

solid answer

~50 s

Only plain `configMap` volume mounts propagate. The kubelet watches the ConfigMap, and when it changes it writes a new timestamped directory inside the volume and atomically flips the `..data` symlink, so readers see a consistent set of files. Latency is roughly the kubelet's sync period plus its watch/cache propagation delay — commonly up to about a minute, not instant. Two consumers never update: - **Environment variables** (`envFrom` or `configMapKeyRef`) are resolved once when the container is created; a process environment cannot be rewritten from outside. - **`subPath` mounts**, which are a one-time copy of a single file rather than a managed directory. A ConfigMap marked immutable is never updated either — that is the point of the flag. And propagation is not reload: the file changing does nothing unless the application watches it, is sent `SIGHUP`, or is restarted. Most teams therefore force a rollout (checksum annotation on the Pod template, or a hashed ConfigMap name) instead of relying on live updates.

code

bash · 7 lines
bash
kubectl exec -it deploy/app -- ls -la /etc/app
# lrwxrwxrwx  ..data -> ..2026_08_12_10_31_00.123456789
# lrwxrwxrwx  application.yaml -> ..data/application.yaml

# edit and watch propagation
kubectl edit configmap app-config
kubectl exec -it deploy/app -- sh -c 'while :; do date; cat /etc/app/application.yaml; sleep 10; done'

go deeper

for a junior

State the rule: volumes update, env vars do not, and it takes up to about a minute.

for a middle

Explain the kubelet's watch plus the atomic ..data symlink swap, name the subPath and immutable exceptions, and note that the app still has to re-read.

for a senior

Argue for forced rollouts over live updates: live config bypasses readiness probes and rollback, and debug it systematically (consumer form, subPath, namespace, exec and look).

for a principal

Treat configuration as a released artefact — hashed names, one change path, canary and revert semantics equal to image rollouts — and weigh that against the operational appeal of hot reload for large fleets.

## The question behind the question Interviewers ask this because "I changed the ConfigMap and nothing happened" is one of the most common Kubernetes support tickets. Answering it well requires separating three distinct steps: the API object changing, the container's view of it changing, and the application acting on it. Only the middle step is Kubernetes' job, and it only happens for some consumption forms. ## What the kubelet actually does When a Pod mounts a `configMap` volume, the kubelet on that node must keep the mounted content in sync with the API object. It does this in the same loop that reconciles the Pod. Modern kubelets use a *watch*-based strategy by default (`configMapAndSecretChangeDetectionStrategy: Watch`): the kubelet holds a watch on the ConfigMaps its Pods reference, and updates arrive as the API server pushes them. Alternative strategies exist — `Cache` (TTL-based, default TTL one minute) and `Get` (read straight through) — and a kubelet configured with `Get` puts noticeably more load on the API server. The write itself is deliberately atomic. The mounted directory is not a flat set of files; it contains a hidden timestamped directory such as `..2026_08_12_10_31_00.123456789/` holding the real files, a symlink `..data` pointing at it, and one symlink per key pointing into `..data`. To publish an update the kubelet writes a brand-new timestamped directory, then swaps `..data` with an atomic rename, then removes the old directory. A reader either sees the entire old set or the entire new set, never a mixture. This also explains why the mount looks odd to tools that walk directories or that dislike symlinks, and why file watchers must watch the *directory* (or handle symlink replacement) rather than the inode of a single file — an inotify watch on the file itself will go silent after the first swap. **Timing.** Total latency is the kubelet's detection delay plus its sync period. In practice people quote "up to about a minute"; the documented worst case is kubelet sync period + cache TTL. It is never synchronous with your `kubectl apply`, so any test that checks the file immediately after the edit will look like a failure. ## What never propagates **Environment variables.** `envFrom.configMapRef` and `env[].valueFrom.configMapKeyRef` are evaluated by the kubelet exactly once, when it constructs the container. The values are handed to the container runtime as part of the process environment. Nothing in Linux lets an outside party rewrite another process's environment, so the only way to change them is to create a new container — that is, delete the Pod or roll the Deployment. **`subPath` mounts.** `volumeMounts[].subPath` exists to project a single file into a directory without hiding that directory's other contents — for example putting `my.cnf` into `/etc/mysql/` without shadowing everything else. It is implemented as a bind mount of one path inside the volume, taken at container start, and it is explicitly documented as not receiving updates. If you need both single-file placement and updates, mount the whole volume in a sibling directory and symlink, or use `subPathExpr`-free layouts, or accept a restart. (Since Kubernetes 1.33, mounting a ConfigMap or Secret volume `readOnly` no longer blocks updates; the `subPath` restriction is the one that persists.) **Immutable ConfigMaps.** Setting `immutable: true` makes the API server reject any change to `data`; the object must be replaced under a new name. Nothing to propagate, by design — and the kubelet stops watching it, which is the performance benefit. ## Propagation is not reload Even a perfectly refreshed file changes nothing if the process read its configuration at startup and never looks again. Options, in rough order of preference: 1. **Force a new Pod.** Put a hash of the ConfigMap content in the Pod template as an annotation, or generate the ConfigMap with a content-hash suffix in its name (Kustomize's `configMapGenerator` does this). Any change alters the Pod template, which triggers a normal rolling update with health checks and rollback. 2. **Native reload.** Some servers watch their config file (Envoy, Prometheus with a reload endpoint, nginx with `SIGHUP`). A tiny sidecar can watch the directory and hit the reload endpoint. 3. **A reload controller.** Operators such as Reloader watch referenced ConfigMaps and patch the Deployment for you. Option 1 is the one to advocate in an interview: it makes configuration part of the versioned, health-checked, rollback-able rollout instead of an invisible side channel. Live volume updates skip readiness probes entirely — a bad config value can be applied to every replica simultaneously with no canary and no automatic revert. ## Debug checklist Is the consumer an env var? Is the mount using `subPath`? Is the ConfigMap immutable? Did you wait a minute? Does `kubectl exec` show the new file content (proving propagation worked and the app is the problem)? Are you editing the right object in the right namespace — a hashed-name generator may have created a *new* ConfigMap the Pod does not reference.

  • Why does an inotify watch on a single mounted ConfigMap file stop firing after the first update?
    The mounted entry is a symlink into a `..data` directory, and the kubelet publishes updates by creating a new directory and atomically re-pointing `..data`. A watch bound to the original inode now follows a path nobody writes to. Watch the mount directory (or re-establish the watch on each event) so you see the symlink replacement.
  • How would you roll back a bad configuration change that was applied through a live volume update?
    You cannot roll it back with `kubectl rollout undo`, because the Deployment's Pod template never changed — you have to re-edit the ConfigMap and wait for propagation again. That asymmetry is the main argument for versioned/hashed ConfigMap names, which make config part of the Deployment revision and therefore revertible with the workload.
  • What is the propagation delay made of, and can you tune it?
    It is the kubelet's change-detection delay plus its sync period. The detection mechanism is set by the kubelet's `configMapAndSecretChangeDetectionStrategy` (`Watch` by default, or `Cache` with a one-minute TTL, or `Get`). You can shorten it with kubelet configuration, but doing so raises API-server load, and no setting makes it synchronous — application design should not depend on the exact latency.

saying these in an interview costs you the question

  • Saying Kubernetes restarts Pods automatically when their ConfigMap changes
  • Expecting environment variables to refresh with the volume
  • Forgetting that subPath mounts are frozen at container start
  • Assuming propagation is instantaneous and declaring it broken after ten seconds
  • Treating a refreshed file as a reloaded application without any watcher or SIGHUP

context