Which pods use this ConfigMap or Secret?
kubectl has no flag that lists the workloads using a ConfigMap or Secret. The
references sit in each pod template, in six places: volumes, projected volumes,
envFrom, env. (in containers and init containers) and
imagePullSecrets. The jq command below reads all of them. After you change the
ConfigMap, files in a mounted volume update by themselves, 16 to 76 seconds later in our test on
GKE 1.36. Environment variables and subPath files keep the old value until the pod
is replaced, so run kubectl rollout restart on what the command finds.
By Ostgate · Updated
Find the workloads that use a ConfigMap or Secret
Set the namespace and the name. For a Secret, change --arg kind configmap to
--arg kind secret. One namespace is enough: “The Pod and the ConfigMap must be in
the same namespace”
(ConfigMaps), and
imagePullSecrets also refers to Secrets in the pod's own namespace.
kubectl -n NAMESPACE get deploy,sts,ds,cronjob,job,pod -o json | jq -r --arg kind configmap --arg name app-config '
def ref: if $kind == "secret" then .secret.secretName // .secret.name else .configMap.name end;
def uses: . as $s
| ($s.volumes[]? | select(ref == $name or any(.projected.sources[]?; ref == $name)) | .name as $v
| (if .projected then "projected" else "volume" end)
+ (if any($s.initContainers[]?, $s.containers[]?; any(.volumeMounts[]?; .name == $v and (.subPath // .subPathExpr))) then "+subPath" else "" end)),
(($s.initContainers[]?, $s.containers[]?) as $c
| ($c.envFrom[]? | select((if $kind == "secret" then .secretRef else .configMapRef end).name == $name) | "envFrom"),
($c.env[]? | select((if $kind == "secret" then .valueFrom.secretKeyRef else .valueFrom.configMapKeyRef end).name == $name) | "env")),
(if $kind == "secret" then $s.imagePullSecrets[]? | select(.name == $name) | "imagePullSecrets" else empty end);
.items[]
| [(if .kind == "Pod" then .spec elif .kind == "CronJob" then .spec.jobTemplate.spec.template.spec else .spec.template.spec end) | uses] as $u
| select($u | length > 0)
| "\(.kind)/\(.metadata.name) \($u | unique | join(" "))"'
Each line names an object and how it uses the ConfigMap. A volume mounted with
subPath prints volume+subPath, because that file never updates in a
running pod. Pods appear next to their Deployment: a pod started before your change still runs
with the old environment, and the list shows which ones exist.
What it found in our test
We ran this on 10 November 2026 on a GKE Autopilot cluster, version 1.36.4-gke.2377000, in a
namespace of its own. One ConfigMap app-config and one Secret
app-secret with dummy values were referenced by small busybox:1.37
workloads, one per kind of reference. A decoy ConfigMap other-config was mounted by
w-other.
Deployment/w-env env Deployment/w-envfrom envFrom Deployment/w-projected projected Deployment/w-subpath volume+subPath Deployment/w-volume volume StatefulSet/s-init envFrom Pod/p-bare env Pod/s-init-0 envFrom Pod/w-env-787cf95bc9-bxprx env Pod/w-envfrom-b65fc7689-zjf87 envFrom Pod/w-projected-57d56b578c-zxcmm projected Pod/w-subpath-bd565f64c-2x4rv volume+subPath Pod/w-volume-9bb4d6455-bg8hh volume
s-init uses the ConfigMap only in an init container. For the Secret, the same
command with --arg kind secret --arg name app-secret also found a DaemonSet with a
Secret volume and a suspended CronJob with a secretKeyRef. A docker-registry Secret
showed up only on the pod that lists it in imagePullSecrets. The decoy appeared
only for its own name, and a name nobody uses printed nothing.
The usual answer to this question is a jsonpath over pods,
{.spec.volumes[*]..configMap.name}. On the same namespace it found 3 of the 7 pods
that use app-config, the volume, subPath and projected ones, and missed
every pod that reads it through env or envFrom.
Where a pod spec references a ConfigMap or Secret
| Field in the pod spec | ConfigMap | Secret |
|---|---|---|
| Volume | volumes[]. |
volumes[]. |
| Projected volume | volumes[]. |
volumes[]. |
| All keys as env vars | containers[]. |
containers[]. |
| One key as an env var | containers[]. |
containers[]. |
| Init containers | the same two env fields under initContainers[] |
the same two env fields under initContainers[] |
| Image pull | none | imagePullSecrets[].name |
A Deployment, StatefulSet, DaemonSet or Job keeps the pod spec under
spec., and a CronJob under
spec.. Expect
kube-root-ca.crt on nearly every pod (all 8 in our namespace): by default
Kubernetes adds a projected volume with the service account token, and that ConfigMap is one of
its sources
(Managing
service accounts).
Does a running pod see the change?
We changed the ConfigMap and the Secret together, twice, and each container printed its values every 5 seconds. The times run from the patch to the first line with the new value.
| Reference | Running pod sees the change | Restart needed |
|---|---|---|
env with configMapKeyRef or secretKeyRef |
No, old value 38 minutes later | Yes |
envFrom |
No, old value 38 minutes later | Yes |
| Volume | Yes, after 71 to 76 s and 16 to 21 s | Only if the app reads the file once at start |
Volume mounted with subPath |
No, old file 38 minutes later | Yes |
| Projected volume | Yes, after 64 to 69 s and 61 to 66 s | Only if the app reads the file once at start |
| Init container env | No, it ran before the change | Yes; it read the new value on restart |
Secrets behaved exactly like ConfigMaps: in every file mount both keys changed in the same
5-second sample. This matches the Kubernetes docs. “ConfigMaps consumed as environment
variables are not updated automatically and require a pod restart”, and “A container using a
ConfigMap as a subPath volume mount will not receive ConfigMap updates”
(ConfigMaps). The
Secrets page says the same of subPath
(Secrets).
The delay for a mounted file “can be as long as the kubelet sync period + cache propagation
delay” (ConfigMaps),
and the kubelet's syncFrequency has “Default: "1m"”
(kubelet
configuration). Our two samples fell between 16 and 76 seconds. If you need the new value
at a known moment, restart instead of waiting.
Restart pods after a ConfigMap change
Save the jq program from the command above (the text between the single
quotes) as used-by.jq. Then list only Deployments, StatefulSets and DaemonSets and
pass the first column to kubectl rollout restart:
kubectl -n NAMESPACE get deploy,sts,ds -o json \ | jq -r --arg kind configmap --arg name app-config -f used-by.jq \ | cut -d" " -f1 \ | xargs kubectl -n NAMESPACE rollout restart
deployment.apps/w-env restarted deployment.apps/w-envfrom restarted deployment.apps/w-projected restarted deployment.apps/w-subpath restarted deployment.apps/w-volume restarted statefulset.apps/s-init restarted
Every new pod printed the new values, the init container included, and each Deployment's pod
template got a kubectl.kubernetes.io/ annotation. Check with
kubectl rollout status. A StatefulSet or DaemonSet may replace only some pods, or
none; kubectl rollout restart on
a StatefulSet or DaemonSet covers why. Right after the restart, kubectl logs
deploy/w-env still picked an old pod that was terminating, so read the new pods by
name.
Two kinds can't be restarted this way. A pod without a controller answers
error: pods "p-bare" restarting is not supported; delete it and create it again
from its manifest. A CronJob answers error: cronjobs.batch "c-nightly" restarting is not
supported; there is nothing running to restart, and its next Job starts new pods.
How Ostgate does it
In Ostgate, select a ConfigMap or Secret in a cluster's tab. In its inspector,
Related has Used by, which names the workloads and pods that reference it.
Restart… on a Deployment, StatefulSet or DaemonSet replaces its pods under its rollout
strategy, as kubectl rollout restart does.
The confirmation names the account, project, cluster and namespace, counts the pods that go down at once, and says when an OnDelete StatefulSet would restart nothing. Ostgate asks the cluster first whether your account may restart the workload; when it may not, Restart… is disabled and says which permission is missing (Restart and delete).
Symptoms and causes
| You see | Cause | Fix |
|---|---|---|
| ConfigMap changed, the app still has the old environment variable | env and envFrom are read once, when the container starts |
kubectl rollout restart |
| A mounted file never changes | It is mounted with subPath |
Restart, or mount the volume as a directory |
| The file changed about a minute later, the app still uses the old value | The app read the file at start | Restart, or make the app reload the file |
A jsonpath over volumes misses workloads |
They use env, envFrom or an init container |
Use the jq command |
restarting is not supported |
A bare pod or a CronJob | Recreate the pod; a CronJob's next Job starts new pods |
kubectl logs deploy/… shows the old value after a restart |
It read a pod that was still terminating | Wait for rollout status, read the new pod by name |
Questions
Do pods pick up a changed Secret?
Only through a mounted volume. A Secret mounted as a volume or a projected volume updates in the running container after the kubelet's next sync; in our test on GKE 1.36 that took 16 to 76 seconds. A Secret read through env, envFrom or a subPath mount keeps the old value until the pod is replaced, for example by kubectl rollout restart.
Why didn't my subPath file update?
Kubernetes does not update subPath mounts of a ConfigMap or Secret. In our test the subPath files still held the old values 38 minutes after the change, while a plain volume mount of the same ConfigMap had updated within 76 seconds. Restart the pods, or mount the whole volume as a directory and point the app at the file inside it.
Is there a kubectl command that lists the deployments using a ConfigMap?
No. kubectl get has no filter by ConfigMap or Secret, and the references sit in each pod template: volumes, projected volume sources, envFrom and env valueFrom in containers and init containers, and imagePullSecrets. Read them with kubectl get -o json and jq, as in the command on this page.
How long does a mounted ConfigMap take to update?
Kubernetes bounds it by the kubelet sync period plus the cache propagation delay, and the kubelet's syncFrequency defaults to 1 minute. On a GKE 1.36 Autopilot node we measured 16 to 76 seconds over two changes, and the ConfigMap and Secret files changed in the same 5-second sample each time.
Ostgate runs on Apple Silicon Macs with macOS 15 or later, with a 7-day trial and no sign-up. Setup is in the GKE docs, and GKE on a Mac lists what the GKE area does.