| Tested on | Rocky Linux 10.2 (Red Quartz) workstation |
|---|---|
| Package | kubectl 1.36.3 |
| Applies to | Any host with kubectl configured; any Kubernetes cluster |
| Cert prep | CKA · CKAD |
| Lab environment | Multi-node kubeadm cluster with containerd — install Kubernetes with kubeadm |
| Privilege | Normal user (no sudo required on the workstation) |
| Scope | When ConfigMap and Secret updates reach running Pods; environment variables versus volume projection versus subPath; application reload versus Pod restart; rollout restart; checksum annotations; and versioned object names. Does not cover object creation syntax, reloader controllers, or zero-downtime guarantees. |
| Related guides | Deployment rolling updates |
You changed a ConfigMap or Secret and want to know when the running application will actually see the new value. The answer depends on how the Pod consumes that object—not on whether the API object updated successfully. This walkthrough runs one controlled experiment in config-update-lab so you can compare environment variables, a normal volume mount, and a subPath file side by side.
Answer First: What Updates Automatically?
| Consumption method | Files or values inside existing container | Pod recreation required |
|---|---|---|
env with configMapKeyRef or secretKeyRef |
Does not change | Yes |
envFrom |
Does not change | Yes |
| Normal ConfigMap volume | Eventually projected into the mount | No for the file itself |
| Normal Secret volume | Eventually projected into the mount | No for the file itself |
ConfigMap or Secret mounted with subPath |
Does not change | Yes |
| Immutable ConfigMap or Secret | Source data cannot be updated | New object/reference required |
Three points follow from this table:
- A projected file update does not guarantee the application reloads it. A process that reads configuration only at startup keeps old in-memory values until it reloads or restarts.
- “No Pod restart” is possible only when both the mount updates and the application rereads the file.
- Updating the ConfigMap in the API is never sufficient proof that the application changed behaviour—verify the mount or environment and the application output.
Object creation syntax lives in Kubernetes ConfigMaps and Kubernetes Secrets. This article focuses on what happens after you change the data.
Build a Controlled Update Lab
The lab uses one ConfigMap and one Deployment with three containers. Each container prints APP_COLOR every five seconds so you can watch behaviour without guessing.
Save app-config.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: config-update-lab
data:
APP_COLOR: blueCreate the namespace:
kubectl create namespace config-update-labSample output:
namespace/config-update-lab createdApply the ConfigMap manifest:
kubectl apply -f app-config.yamlSample output:
configmap/app-config createdSave update-lab.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: config-update-lab
namespace: config-update-lab
spec:
replicas: 1
selector:
matchLabels:
app: config-update-lab
template:
metadata:
labels:
app: config-update-lab
spec:
containers:
- name: env-test
image: busybox:1.36
command: ["sh", "-c", "while true; do echo env APP_COLOR=$APP_COLOR; sleep 5; done"]
env:
- name: APP_COLOR
valueFrom:
configMapKeyRef:
name: app-config
key: APP_COLOR
- name: vol-test
image: busybox:1.36
command: ["sh", "-c", "while true; do echo vol APP_COLOR=$(cat /etc/config/APP_COLOR); sleep 5; done"]
volumeMounts:
- name: cfg
mountPath: /etc/config
- name: subpath-test
image: busybox:1.36
command: ["sh", "-c", "while true; do echo subpath APP_COLOR=$(cat /etc/app-color); sleep 5; done"]
volumeMounts:
- name: cfg
mountPath: /etc/app-color
subPath: APP_COLOR
volumes:
- name: cfg
configMap:
name: app-configApply the Deployment and wait for the Pod:
kubectl apply -f update-lab.yamlSample output:
deployment.apps/config-update-lab createdWait for the Pod to become ready:
kubectl rollout status deployment/config-update-lab -n config-update-lab --timeout=90sStore the exact Pod and UID:
POD=$(kubectl get pods -n config-update-lab -l app=config-update-lab -o jsonpath='{.items[0].metadata.name}')
OLD_UID=$(kubectl get pod "$POD" -n config-update-lab -o jsonpath='{.metadata.uid}')
printf 'pod=%s uid=%s\n' "$POD" "$OLD_UID"Sample output:
pod=config-update-lab-7f9464dcd-cffjf uid=085eea95-35db-47b0-bfa4-4ddbf0fc3f0dStore the ConfigMap resource version before you change anything:
OLD_RV=$(kubectl get configmap app-config -n config-update-lab -o jsonpath='{.metadata.resourceVersion}')
printf '%s\n' "$OLD_RV"Sample output:
460690All three containers should print blue in their logs:
kubectl logs "$POD" -n config-update-lab -c env-test --tail=1Sample output:
env APP_COLOR=bluekubectl logs "$POD" -n config-update-lab -c vol-test --tail=1Sample output:
vol APP_COLOR=bluekubectl logs "$POD" -n config-update-lab -c subpath-test --tail=1Sample output:
subpath APP_COLOR=blueCompare Update Behaviour
Update the Source ConfigMap
Change APP_COLOR to green in app-config.yaml:
data:
APP_COLOR: greenApply the manifest:
kubectl apply -f app-config.yamlSample output:
configmap/app-config configuredConfirm the object's resourceVersion changed:
NEW_RV=$(kubectl get configmap app-config -n config-update-lab -o jsonpath='{.metadata.resourceVersion}')
printf 'old_rv=%s new_rv=%s\n' "$OLD_RV" "$NEW_RV"Sample output:
old_rv=460690 new_rv=460726resourceVersion represents the stored version of the object and should not be treated as an application revision number. The ConfigMap now holds green. The question is whether each container sees it.
Environment Variables Stay Unchanged
Container environment variables are assembled when the container starts. The kubelet copies values from referenced ConfigMaps and Secrets into the process environment at that moment. Updating the source object later does not rewrite the environment of a running process.
envFrom, configMapKeyRef, and secretKeyRef all follow this startup-time behaviour. A new Pod or restarted container is required to receive new environment values.
Five seconds after the ConfigMap update, the env container still reports blue:
kubectl exec "$POD" -n config-update-lab -c env-test -- printenv APP_COLORSample output:
blueThe ConfigMap resourceVersion changed, but the existing container process still holds the value from Pod creation. The Pod UID also stays the same—no recreation occurred:
kubectl get pod "$POD" -n config-update-lab -o jsonpath='{.metadata.uid}{"\n"}'Sample output:
085eea95-35db-47b0-bfa4-4ddbf0fc3f0dNormal Volume Files Eventually Update
When a ConfigMap or Secret is mounted as a normal volume, the kubelet eventually refreshes projected file content. The update is not immediate. Delay depends on kubelet synchronization and its configured configMapAndSecretChangeDetectionStrategy (watch, TTL-based cache, or direct API access). Do not assume a fixed interval such as exactly one or two minutes.
The Pod UID and container process can remain unchanged while the file on disk changes. Wait for the projected file instead of assuming an immediate update:
VALUE=""
for attempt in {1..90}; do
VALUE=$(kubectl exec "$POD" -n config-update-lab -c vol-test -- cat /etc/config/APP_COLOR)
[[ "$VALUE" == "green" ]] && break
sleep 2
done
if [[ "$VALUE" != "green" ]]; then
echo "Timed out waiting for the ConfigMap volume update" >&2
exit 1
fi
printf '%s\n' "$VALUE"Sample output:
greenThis proves the update while allowing up to three minutes for different kubelet configurations.
The env container still holds the startup value:
kubectl exec "$POD" -n config-update-lab -c env-test -- printenv APP_COLORSample output:
blueThe volume container log eventually prints vol APP_COLOR=green on its next read cycle. Secret volumes follow the same eventual projection model. Secret-derived environment variables remain unchanged in existing processes, while normal Secret volume files are eventually refreshed. Secret files mounted using subPath do not receive automated updates. These behaviours are documented for Secret environment variables, normal Secret volumes, and subPath mounts.
subPath Files Stay Unchanged
A subPath mount binds one file at container start. It does not follow later ConfigMap or Secret projection changes. In the lab, subpath-test still reads blue after the ConfigMap holds green:
kubectl exec "$POD" -n config-update-lab -c subpath-test -- cat /etc/app-colorSample output:
blueRecreating the Pod is required for a subPath mount to use new content. General subPath syntax and directory-preservation patterns are covered in subPath volume examples—this article focuses on the update limitation.
File Update vs Application Reload
Kubernetes can change a mounted file, but the application decides whether that change matters. Three common behaviours:
| Application behaviour | Result after projected file changes |
|---|---|
| Reads file for every request | Uses new content automatically |
| Watches the file or supports reload | Uses new content after its reload mechanism |
| Reads configuration only at startup | Continues using old in-memory configuration |
This distinction is the central message of the article. A successful kubectl apply on the ConfigMap plus an updated file on disk still leaves a startup-only application serving old behaviour until you reload or restart it.
Apply Configuration Changes
Reload the Application
When the mount updates and the application supports it, you can reload without a new Pod:
- Built-in file watcher in the application
- HTTP or admin reload endpoint
- Signal such as
SIGHUPwhere the application handles it - Sidecar that detects file changes and triggers the supported reload action
Kubernetes only updates mounted data. It does not know how an arbitrary application reloads configuration. Do not kill PID 1 as a general reload method. That terminates the container rather than asking the application to reload configuration. The kubelet may restart it according to the Pod restart policy, but this is not a controlled workload rollout and may cause an interruption.
Restart the Workload
When environment variables or subPath mounts must pick up new values, restart the workload so new containers start with current data. kubectl rollout restart adds a Pod-template annotation that triggers a normal rolling replacement:
kubectl rollout restart deployment/config-update-lab -n config-update-labSample output:
deployment.apps/config-update-lab restartedTrack the rolling replacement:
kubectl rollout status deployment/config-update-lab -n config-update-lab --timeout=90sSample output:
deployment "config-update-lab" successfully rolled outResolve the new Pod rather than relying only on selector-based logs:
NEW_POD=$(kubectl get pods \
-n config-update-lab \
-l app=config-update-lab \
--sort-by=.metadata.creationTimestamp \
-o custom-columns=NAME:.metadata.name \
--no-headers | tail \
-1)
NEW_UID=$(kubectl get pod "$NEW_POD" -n config-update-lab -o jsonpath='{.metadata.uid}')
printf 'old_uid=%s new_uid=%s\n' "$OLD_UID" "$NEW_UID"Sample output:
old_uid=085eea95-35db-47b0-bfa4-4ddbf0fc3f0d new_uid=a1b2c3d4-e5f6-7890-abcd-ef1234567890After the rollout, all three lab containers print green—including env-test and subpath-test, which could not update in place:
kubectl logs "$NEW_POD" -n config-update-lab -c env-test --tail=1Sample output:
env APP_COLOR=greenThe subPath container also picks up green only after recreation:
kubectl logs "$NEW_POD" -n config-update-lab -c subpath-test --tail=1Sample output:
subpath APP_COLOR=greenkubectl rollout restart works for Deployments, StatefulSets, and DaemonSets.
Automate Configuration Rollouts
Checksum Annotations
Changing only the data inside a referenced ConfigMap or Secret does not modify the workload's Pod template. Controllers create replacement Pods when spec.template changes, which is why deployment tools commonly place a configuration checksum in a Pod-template annotation.
Helm charts often use a template like this:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}When the ConfigMap content changes, the rendered checksum changes, the Pod template changes, and the workload controller performs a normal rollout. This is a packaging convention—not a built-in Kubernetes feature. Plain YAML or CI pipelines must compute and inject the checksum with external tooling. See Kubernetes Helm charts for how release templates manage this pattern.
Versioned ConfigMap and Secret Names
Explicit versioning avoids patching data in place and works well with immutable objects:
app-config-v1→app-config-v2- Update the Deployment to reference the new name
Benefits:
- Configuration version is visible in the object name
- Works with
immutable: true - Produces a Pod-template change when the reference updates
- Rollback is a template revert to the earlier name
Pair this with a normal rollout when env vars or subPath mounts must refresh.
Verify the Effective Configuration
Check three layers—not just the API object:
- Source ConfigMap or Secret contains the intended value (
kubectl get … -o yaml) - Mounted file or environment variable inside the container matches (
kubectl exec, application logs) - Application behaviour confirms reload or restart (HTTP response, metrics, functional test)
Treating the ConfigMap alone as proof of change is a common mistake. The lab made this visible: after the API showed green, only the volume mount caught up without a Pod restart—and even that required waiting for kubelet projection.
Common Mistakes
| Symptom | Likely cause | Fix |
|---|---|---|
ConfigMap shows new value but printenv is old |
Value consumed through env or envFrom |
kubectl rollout restart the workload |
| File on disk is new but app behaviour unchanged | Application reads config only at startup | Reload through app mechanism or restart containers |
subPath file never updates |
subPath does not receive projections |
Recreate Pod or avoid subPath when live refresh is required |
| Patched immutable ConfigMap fails | immutable: true blocks edits |
Create new object name and update the reference |
| Volume updated slowly | Kubelet sync delay | Wait and re-check; do not assume instant propagation |
What's Next
- Kubernetes Secrets with Examples
- Kubernetes command and args, Environment Variables and Downward API
- Kubernetes Requests, Limits and QoS Classes
References
- ConfigMaps — mounted ConfigMaps are updated automatically — Kubernetes documentation
- Secrets — mounted Secrets are updated automatically — Kubernetes documentation
- Configure a Pod to use a ConfigMap — official task guide
Summary
ConfigMap and Secret updates do not reach every running container the same way. Environment variables from configMapKeyRef, secretKeyRef, and envFrom are fixed at container start—patching the source object leaves existing processes unchanged until new Pods are created. Normal ConfigMap and Secret volumes are eventually refreshed by the kubelet, but the delay is not guaranteed to be immediate, and subPath mounts never receive live updates.
Even when the mounted file changes, an application that reads configuration only at startup keeps serving old behaviour until it reloads or the container restarts. Use application-supported reload mechanisms when the mount path can update in place; use kubectl rollout restart, checksum annotations, or versioned object names when env vars or subPath mounts must change. Always verify inside the container and through application behaviour—not only at the API object.

