Kubernetes Error Guide: 'CreateContainerConfigError' Missing ConfigMap or Secret
Fix the Kubernetes CreateContainerConfigError: diagnose missing ConfigMaps and Secrets, absent keys, wrong namespaces, ordering races, and bad envFrom/valueFrom references.
- #kubernetes
- #troubleshooting
- #errors
- #configmap
Stuck on this Kubernetes & Helm error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
CreateContainerConfigError means the kubelet successfully pulled the image but could not assemble the container’s configuration, because something the pod spec references — a ConfigMap, a Secret, or a specific key inside one — does not exist. The pod is scheduled and the image is present, but the container never starts.
You will see this in kubectl get pods:
NAME READY STATUS RESTARTS AGE
api-6c8f9d7b54-q9w2r 0/1 CreateContainerConfigError 0 47s
And the underlying reason in the pod’s events:
Error: configmap "api-config" not found
Or, when the object exists but a referenced key does not:
Error: couldn't find key DATABASE_URL in ConfigMap shop/api-config
It occurs whenever a container references config via envFrom, env.valueFrom.configMapKeyRef, env.valueFrom.secretKeyRef, or a projected/volume mount, and that source is missing at container-create time. Unlike an image pull error, the scheduling and image are fine — only the config wiring is broken.
Symptoms
- Pod status is
CreateContainerConfigError(notPending, notImagePullBackOff). kubectl describe podshowsconfigmap "<name>" not foundorcouldn't find key <KEY>.- The container never reaches
Running; restarts stay at 0 because it never started. - The Deployment rolls out one bad pod and the rollout stalls.
kubectl get pods -n shop
NAME READY STATUS RESTARTS AGE
api-6c8f9d7b54-q9w2r 0/1 CreateContainerConfigError 0 47s
api-5b7c8d6f43-h2k8n 1/1 Running 0 3h
kubectl describe pod api-6c8f9d7b54-q9w2r -n shop | sed -n '/Events/,$p'
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Pulled 12s (x4 over 47s) kubelet Container image "registry.example.com/api:1.8.2" already present on machine
Warning Failed 12s (x4 over 47s) kubelet Error: configmap "api-config" not found
Common Root Causes
1. envFrom references a ConfigMap/Secret that does not exist
The pod uses envFrom.configMapRef (or secretRef) naming an object that was never created (or was deleted/renamed).
kubectl get configmap -n shop
NAME DATA AGE
app-config 6 3h
kube-root-ca.crt 1 3h
The pod references api-config, but only app-config exists — a rename that was not applied to the Deployment.
2. A referenced key is missing inside an existing object
The ConfigMap/Secret exists, but configMapKeyRef/secretKeyRef names a key that is not present.
kubectl get configmap app-config -n shop -o jsonpath='{.data}' | tr ',' '\n'
{"APP_PORT":"8080"
"LOG_LEVEL":"info"
"FEATURE_FLAGS":"beta"}
The container asks for DATABASE_URL via configMapKeyRef, but the ConfigMap has no such key, producing couldn't find key DATABASE_URL.
3. The object is in the wrong namespace
ConfigMaps and Secrets are namespaced; a pod can only reference one in its own namespace. The object exists, but in default instead of shop.
kubectl get secret db-credentials --all-namespaces
NAMESPACE NAME TYPE DATA AGE
default db-credentials Opaque 2 5h
The pod runs in shop, so the shop-namespaced lookup fails even though the secret exists elsewhere.
4. The Secret/ConfigMap is created after the pod (ordering race)
In a fresh deploy, the pod can be created before its Secret (e.g., an external-secrets sync, a Helm hook, or a separate kubectl apply step has not run yet).
kubectl get secret api-tls -n shop
Error from server (NotFound): secrets "api-tls" not found
The pod is in CreateContainerConfigError now; once the secret lands, the kubelet retries and the container starts — but only if nothing else is wrong.
5. Missing optional: true for an intentionally absent source
If a reference is genuinely optional but the spec omits optional: true, Kubernetes treats the missing source as a hard error instead of skipping it.
kubectl get pod api-6c8f9d7b54-q9w2r -n shop -o jsonpath='{.spec.containers[0].envFrom}'; echo
[{"configMapRef":{"name":"feature-overrides"}}]
feature-overrides is meant to be present only in staging; without "optional":true on the ref, its absence in prod breaks container creation.
6. Typo in the env name, key, or valueFrom reference
A simple typo in the ConfigMap/Secret name or the key string sends the lookup to something that does not exist.
kubectl get pod api-6c8f9d7b54-q9w2r -n shop -o jsonpath='{range .spec.containers[0].env[*]}{.name}{" <- "}{.valueFrom.secretKeyRef.name}{"/"}{.valueFrom.secretKeyRef.key}{"\n"}{end}'
DB_PASSWORD <- db-credentials/postgres-pasword
The key postgres-pasword is a typo for postgres-password, so the lookup yields couldn't find key.
Diagnostic Workflow
Step 1: Confirm the status and read the exact error
kubectl get pod <POD> -n <NS>
kubectl describe pod <POD> -n <NS> | sed -n '/Events/,$p'
The event message tells you whether it is a missing object (... not found) or a missing key (couldn't find key ...).
Step 2: Identify every config source the container references
kubectl get pod <POD> -n <NS> -o yaml | grep -nE 'configMapRef|secretRef|configMapKeyRef|secretKeyRef|name:|key:|optional'
List the exact names and keys the spec expects.
Step 3: Verify the objects exist in the pod’s namespace
kubectl get configmap -n <NS>
kubectl get secret -n <NS>
A name in the spec that is not in this list is your missing object. If you suspect a namespace mistake, run with --all-namespaces.
Step 4: Verify the specific keys exist
kubectl get configmap <NAME> -n <NS> -o jsonpath='{.data}' | tr ',' '\n'
kubectl get secret <NAME> -n <NS> -o jsonpath='{.data}' | tr ',' '\n'
Compare the key names against the configMapKeyRef/secretKeyRef from Step 2 — watch for typos and case.
Step 5: Fix the source, then let the kubelet retry
# create the missing ConfigMap/Secret, or fix the reference, then:
kubectl rollout restart deploy/<DEPLOY> -n <NS>
kubectl get pods -n <NS> -w
The kubelet retries CreateContainerConfigError automatically, but a rollout restart guarantees a clean pod once the source is corrected.
Example Root Cause Analysis
A new api Deployment in namespace shop rolls out one pod stuck in CreateContainerConfigError.
kubectl describe pod shows the image is present and the failure is config:
Normal Pulled kubelet Container image "registry.example.com/api:1.8.2" already present on machine
Warning Failed kubelet Error: couldn't find key DATABASE_URL in ConfigMap shop/app-config
So the object exists; a key does not. Listing the spec’s references:
kubectl get pod api-6c8f9d7b54-q9w2r -n shop -o jsonpath='{range .spec.containers[0].env[*]}{.name}{" <- "}{.valueFrom.configMapKeyRef.name}{"/"}{.valueFrom.configMapKeyRef.key}{"\n"}{end}'
DATABASE_URL <- app-config/DATABASE_URL
The container wants the key DATABASE_URL from app-config. Checking what the ConfigMap actually contains:
kubectl get configmap app-config -n shop -o jsonpath='{.data}' | tr ',' '\n'
{"APP_PORT":"8080"
"LOG_LEVEL":"info"
"DB_URL":"postgres://db:5432/shop"}
The connection string is stored under DB_URL, but the Deployment references DATABASE_URL. The key name drifted between the config and the manifest.
Fix: align the names — either add a DATABASE_URL key to the ConfigMap or point the configMapKeyRef.key at DB_URL — then restart the rollout:
kubectl patch configmap app-config -n shop --type merge \
-p '{"data":{"DATABASE_URL":"postgres://db:5432/shop"}}'
kubectl rollout restart deploy/api -n shop
The kubelet finds the key, the container is configured, and the pod reaches Running.
Prevention Best Practices
- Apply ConfigMaps and Secrets before the workloads that reference them (Helm hooks, Kustomize ordering, or
kubectl apply -fof config first) to avoid ordering races. - Keep config object names and keys under version control alongside the Deployment so a rename in one place forces the matching change in the other.
- Mark genuinely optional references with
optional: trueso an absent source is skipped rather than fatal across environments. - Remember that ConfigMaps and Secrets are namespaced — a pod can only reference ones in its own namespace; never assume a
defaultobject is visible elsewhere. - Validate manifests in CI (e.g.,
kubeconformplus a check that everyconfigMapKeyRef/secretKeyRefresolves) before merge. More patterns are in the Kubernetes & Helm guides. - Prefer
envFromwith explicit, reviewed ConfigMaps over scattered per-keyvalueFromreferences to reduce typo surface.
Quick Command Reference
# Confirm status and the exact failure message
kubectl get pod <POD> -n <NS>
kubectl describe pod <POD> -n <NS> | sed -n '/Events/,$p'
# List every config source the container references
kubectl get pod <POD> -n <NS> -o yaml | grep -nE 'configMapRef|secretRef|configMapKeyRef|secretKeyRef|name:|key:|optional'
# Do the objects exist (in this namespace)?
kubectl get configmap -n <NS>
kubectl get secret -n <NS>
kubectl get configmap <NAME> --all-namespaces
# Do the specific keys exist?
kubectl get configmap <NAME> -n <NS> -o jsonpath='{.data}' | tr ',' '\n'
kubectl get secret <NAME> -n <NS> -o jsonpath='{.data}' | tr ',' '\n'
# Fix the source, then force a clean pod
kubectl rollout restart deploy/<DEPLOY> -n <NS>
kubectl get pods -n <NS> -w
Conclusion
CreateContainerConfigError means the image is fine but the kubelet could not build the container’s config because a referenced ConfigMap, Secret, or key is missing. The usual root causes:
envFrom/configMapRef/secretRefnames an object that does not exist.- The object exists, but a
configMapKeyRef/secretKeyRefkey is absent. - The object lives in the wrong namespace (references are namespace-local).
- The Secret/ConfigMap is created after the pod — an ordering race.
- A genuinely optional reference is missing
optional: true. - A typo in the object name or key string sends the lookup nowhere.
Read the event message first: ... not found points to a missing object, couldn't find key ... points to a missing key — then verify names, keys, and namespace against the spec.
Fixed it? Get 500 Kubernetes & Helm & DevOps AI prompts — free
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.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
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.