Kubernetes CreateContainerConfigError: Causes, Troubleshooting, and Fixes

Kubernetes Troubleshooting

Introduction

Your pod is stuck. The deployment rolled out successfully, but containers refuse to start. You run kubectl get pods and see that dreaded CreateContainerConfigError status.

CreateContainerConfigError occurs when Kubernetes cannot build the container configuration before launching the process. This happens during the pre-start phase—the container runtime never executes your application code. Your app logs? Empty. The issue lies in how Kubernetes assembles configuration data from ConfigMaps, Secrets, or volume mounts.

Key Distinction: This isn’t a runtime crash. The container never started, so traditional debugging (checking logs, exec into pod) won’t work.

Error Flow Visualization

graph TD
    A[Pod Creation Request] --> B{Configuration Valid?}
    B -->|No| C[CreateContainerConfigError]
    B -->|Yes| D[Container Runtime]
    D --> E{Runtime Success?}
    E -->|No| F[CreateContainerError]
    E -->|Yes| G[Running Pod]
    
    C --> H[Check ConfigMaps]
    C --> I[Check Secrets]
    C --> J[Check Volume Mounts]
    C --> K[Check RBAC]
No
Yes
No
Yes
Pod Creation Request
Configuration Valid?
CreateContainerConfigError
Container Runtime
Runtime Success?
CreateContainerError
Running Pod
Check ConfigMaps
Check Secrets
Check Volume Mounts
Check RBAC

Common Causes

1. Missing ConfigMap Reference

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        envFrom:
        - configMapRef:
            name: app-config  # ❌ This ConfigMap doesn't exist

Error Output:

kubectl describe pod web-app-xyz
Events:
  Warning  Failed  CreateContainerConfigError  
  configmap "app-config" not found

2. Secret Key Mismatch

# pod-with-secret.yaml
apiVersion: v1
kind: Pod
metadata:
  name: database-client
spec:
  containers:
  - name: psql
    image: postgres:14
    env:
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: password  # ❌ Actual key is 'db-password'

Verification Commands:

# Check if Secret exists
kubectl get secret db-credentials -o json | jq '.data | keys'

# Output shows actual keys
[
  "db-password",
  "username"
]

3. Volume Mount Configuration Issue

# deployment-with-volume.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: config-reader
spec:
  template:
    spec:
      containers:
      - name: app
        image: busybox
        command: ['sh', '-c', 'cat /config/settings.json']
        volumeMounts:
        - name: config-volume
          mountPath: /config
          subPath: app-settings.json  # ❌ subPath doesn't exist in ConfigMap
      volumes:
      - name: config-volume
        configMap:
          name: application-config

Troubleshooting Decision Tree

flowchart TD
    Start[CreateContainerConfigError] --> Describe[kubectl describe pod]
    Describe --> CheckEvents{Events Show?}
    
    CheckEvents -->|ConfigMap not found| VerifyCM[kubectl get configmap]
    CheckEvents -->|Secret not found| VerifySecret[kubectl get secret]
    CheckEvents -->|Key reference failed| CheckKeys[Verify key names]
    CheckEvents -->|Mount failed| CheckVolumes[Inspect volumeMounts]
    
    VerifyCM --> CMExists{Exists?}
    CMExists -->|No| CreateCM[Create ConfigMap]
    CMExists -->|Yes| CheckNamespace[Wrong namespace?]
    
    VerifySecret --> SecretExists{Exists?}
    SecretExists -->|No| CreateSecret[Create Secret]
    SecretExists -->|Yes| CheckKeyName[Verify key in Secret]
    
    CheckKeys --> FixManifest[Update pod spec]
    CheckVolumes --> FixVolume[Correct mount path/subPath]
    
    CreateCM --> Restart[Delete pod or rollout restart]
    CreateSecret --> Restart
    FixManifest --> Restart
    FixVolume --> Restart
    CheckNamespace --> Restart
    CheckKeyName --> Restart
ConfigMap not foundSecret not foundKey reference failedMount failedNoYesNoYesCreateContainerConfigErrorkubectl describe podEvents Show?kubectl get configmapkubectl get secretVerify key namesInspect volumeMountsExists?Create ConfigMapWrong namespace?Exists?Create SecretVerify key in SecretUpdate pod specCorrect mount path/subPathDelete pod or rollout restart

Step-by-Step Resolution

Step 1: Identify the Error

# Check pod status
kubectl get pods -n production

NAME                    READY   STATUS                       RESTARTS   AGE
web-app-7d9c8b-xyz      0/1     CreateContainerConfigError   0          2m

Step 2: Extract Event Details

# Get detailed pod information
kubectl describe pod web-app-7d9c8b-xyz -n production

Critical Section – Events:

Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  3m                 default-scheduler  Successfully assigned production/web-app-7d9c8b-xyz to node-1
  Warning  Failed     2m (x5 over 3m)    kubelet            Error: configmap "app-config" not found

Step 3: Audit Configuration Resources

Comprehensive Check Script:

#!/bin/bash
# troubleshoot-pod.sh

POD_NAME=$1
NAMESPACE=${2:-default}

echo "=== Pod Status ==="
kubectl get pod $POD_NAME -n $NAMESPACE

echo -e "\n=== Events ==="
kubectl describe pod $POD_NAME -n $NAMESPACE | grep -A 20 "Events:"

echo -e "\n=== Referenced ConfigMaps ==="
kubectl get pod $POD_NAME -n $NAMESPACE -o json | \
  jq -r '.spec.volumes[]?.configMap.name // empty' | \
  while read cm; do
    echo "Checking ConfigMap: $cm"
    kubectl get configmap $cm -n $NAMESPACE 2>&1
  done

echo -e "\n=== Referenced Secrets ==="
kubectl get pod $POD_NAME -n $NAMESPACE -o json | \
  jq -r '.spec.volumes[]?.secret.secretName // empty' | \
  while read secret; do
    echo "Checking Secret: $secret"
    kubectl get secret $secret -n $NAMESPACE 2>&1
  done

echo -e "\n=== Environment Variables from ConfigMaps ==="
kubectl get pod $POD_NAME -n $NAMESPACE -o json | \
  jq -r '.spec.containers[].envFrom[]?.configMapRef.name // empty'

echo -e "\n=== Environment Variables from Secrets ==="
kubectl get pod $POD_NAME -n $NAMESPACE -o json | \
  jq -r '.spec.containers[].envFrom[]?.secretKeyRef.name // empty'

Usage:

chmod +x troubleshoot-pod.sh
./troubleshoot-pod.sh web-app-7d9c8b-xyz production

Fix Scenarios

Scenario 1: Create Missing ConfigMap

# Quick inline creation
kubectl create configmap app-config \
  --from-literal=API_URL=https://api.example.com \
  --from-literal=LOG_LEVEL=info \
  -n production

Or from file:

# config.properties
API_URL=https://api.example.com
LOG_LEVEL=info
MAX_CONNECTIONS=100

# Create from file
kubectl create configmap app-config \
  --from-file=config.properties \
  -n production

Declarative approach:

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: production
data:
  API_URL: "https://api.example.com"
  LOG_LEVEL: "info"
  config.json: |
    {
      "database": {
        "host": "db.internal",
        "port": 5432
      },
      "cache": {
        "ttl": 3600
      }
    }
kubectl apply -f configmap.yaml

Scenario 2: Fix Secret Key Reference

Current broken configuration:

env:
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: db-credentials
      key: password  # ❌ Wrong key

Check actual keys:

kubectl get secret db-credentials -o jsonpath='{.data}' | jq 'keys'
# Output: ["db-password", "username"]

Corrected configuration:

env:
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: db-credentials
      key: db-password  # ✅ Correct key

Apply fix:

kubectl patch deployment web-app -n production --type='json' \
  -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/env/0/valueFrom/secretKeyRef/key", "value":"db-password"}]'

Scenario 3: Volume Mount SubPath Issue

Problematic manifest:

volumeMounts:
- name: config-volume
  mountPath: /app/config
  subPath: application.conf  # ❌ File doesn't exist in ConfigMap

volumes:
- name: config-volume
  configMap:
    name: app-settings

Check ConfigMap contents:

kubectl get configmap app-settings -o json | jq '.data | keys'
# Output: ["app.conf", "database.conf"]

Fix options:

Option A – Remove subPath:

volumeMounts:
- name: config-volume
  mountPath: /app/config
  # Mount entire ConfigMap

Option B – Use correct filename:

volumeMounts:
- name: config-volume
  mountPath: /app/config/application.conf
  subPath: app.conf  # ✅ Matches actual key

Scenario 4: RBAC Permission Issues

Error message:

Error: secrets "api-keys" is forbidden: 
User "system:serviceaccount:production:default" cannot get resource "secrets"

Grant access:

# rbac-fix.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-service-account
  namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-reader
  namespace: production
rules:
- apiGroups: [""]
  resources: ["secrets", "configmaps"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-secrets
  namespace: production
subjects:
- kind: ServiceAccount
  name: app-service-account
  namespace: production
roleRef:
  kind: Role
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

Update Deployment to use ServiceAccount:

spec:
  template:
    spec:
      serviceAccountName: app-service-account
      containers:
      - name: app
        image: myapp:1.0
kubectl apply -f rbac-fix.yaml
kubectl rollout restart deployment web-app -n production

Error Comparison Matrix

graph LR
    A[Pod Start Process] --> B{Config Assembly}
    B -->|Failed| C[CreateContainerConfigError]
    B -->|Success| D{Runtime Creation}
    D -->|Failed| E[CreateContainerError]
    D -->|Success| F{App Execution}
    F -->|Failed| G[CrashLoopBackOff]
    
    style C fill:#ff6b6b
    style E fill:#ffa94d
    style G fill:#ffd43b
    style F fill:#51cf66
Failed
Success
Failed
Success
Failed
Pod Start Process
Config Assembly
CreateContainerConfigError
Runtime Creation
CreateContainerError
App Execution
CrashLoopBackOff
Error TypePhaseContainer Started?Check Logs?Common Causes
CreateContainerConfigErrorPre-start❌ No❌ UselessMissing ConfigMap/Secret, key mismatch, volume mount error
CreateContainerErrorRuntime init❌ No❌ EmptyInvalid command, image pull failure, resource limits
CrashLoopBackOffPost-start✅ Yes✅ CheckApplication crash, missing dependencies, port conflicts

Automated Detection Script

#!/bin/bash
# detect-config-errors.sh

NAMESPACE=${1:-default}

echo "Scanning for CreateContainerConfigError in namespace: $NAMESPACE"
echo "================================================================"

kubectl get pods -n $NAMESPACE --field-selector=status.phase!=Running,status.phase!=Succeeded -o json | \
jq -r '.items[] | 
  select(.status.containerStatuses != null) | 
  select(.status.containerStatuses[].state.waiting.reason == "CreateContainerConfigError") | 
  {
    pod: .metadata.name,
    namespace: .metadata.namespace,
    containers: [.status.containerStatuses[].name],
    message: .status.containerStatuses[].state.waiting.message
  }' | \
jq -s '.' | \
jq -r '.[] | "Pod: \(.pod)\nContainers: \(.containers | join(", "))\nError: \(.message)\n---"'

echo ""
echo "Detailed Troubleshooting:"
echo "========================="

kubectl get pods -n $NAMESPACE --field-selector=status.phase!=Running -o name | \
while read pod; do
  POD_NAME=$(echo $pod | cut -d'/' -f2)
  ERROR_STATUS=$(kubectl get $pod -n $NAMESPACE -o jsonpath='{.status.containerStatuses[*].state.waiting.reason}')
  
  if [[ $ERROR_STATUS == *"CreateContainerConfigError"* ]]; then
    echo ""
    echo "🔍 Analyzing: $POD_NAME"
    echo "Events:"
    kubectl describe pod $POD_NAME -n $NAMESPACE | grep -A 10 "Events:" | grep -E "(ConfigMap|Secret|not found|forbidden)"
  fi
done

Prevention Strategies

1. Pre-Deployment Validation

#!/bin/bash
# validate-resources.sh

MANIFEST=$1
NAMESPACE=${2:-default}

echo "Validating manifest: $MANIFEST"

# Extract ConfigMap references
CONFIGMAPS=$(yq eval '.spec.template.spec.volumes[].configMap.name // empty' $MANIFEST 2>/dev/null)

for cm in $CONFIGMAPS; do
  if kubectl get configmap $cm -n $NAMESPACE &>/dev/null; then
    echo "✅ ConfigMap $cm exists"
  else
    echo "❌ ConfigMap $cm NOT FOUND"
    exit 1
  fi
done

# Extract Secret references
SECRETS=$(yq eval '.spec.template.spec.volumes[].secret.secretName // empty' $MANIFEST 2>/dev/null)

for secret in $SECRETS; do
  if kubectl get secret $secret -n $NAMESPACE &>/dev/null; then
    echo "✅ Secret $secret exists"
  else
    echo "❌ Secret $secret NOT FOUND"
    exit 1
  fi
done

echo "✅ All resources validated"

Usage in CI/CD:

./validate-resources.sh deployment.yaml production && \
kubectl apply -f deployment.yaml

2. Helm Chart Dependencies

# Chart.yaml
apiVersion: v2
name: web-application
version: 1.0.0
dependencies:
  - name: common-config
    version: "1.x.x"
    repository: "@company-repo"
# values.yaml
configMaps:
  create: true
  data:
    API_URL: "https://api.example.com"
    LOG_LEVEL: "info"

secrets:
  create: true
  data:
    DB_PASSWORD: "{{ .Values.database.password }}"
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}
spec:
  template:
    spec:
      containers:
      - name: app
        image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
        envFrom:
        - configMapRef:
            name: {{ .Release.Name }}-config
        {{- if .Values.secrets.create }}
        - secretRef:
            name: {{ .Release.Name }}-secrets
        {{- end }}

3. Kustomize Overlay Strategy

├── base/
│   ├── deployment.yaml
│   ├── configmap.yaml
│   ├── secret.yaml
│   └── kustomization.yaml
└── overlays/
    ├── dev/
    │   ├── configmap.yaml
    │   └── kustomization.yaml
    └── prod/
        ├── configmap.yaml
        └── kustomization.yaml

base/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- deployment.yaml
- configmap.yaml
- secret.yaml

configMapGenerator:
- name: app-config
  literals:
  - APP_ENV=base

overlays/prod/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

bases:
- ../../base

configMapGenerator:
- name: app-config
  behavior: merge
  literals:
  - APP_ENV=production
  - API_URL=https://api.prod.example.com
# Deploy with guaranteed resource creation
kustomize build overlays/prod | kubectl apply -f -

4. Admission Controller Validation

# validating-webhook.yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: config-validator
webhooks:
- name: validate-config-refs.example.com
  admissionReviewVersions: ["v1"]
  clientConfig:
    service:
      name: validation-service
      namespace: kube-system
      path: "/validate"
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: ["apps"]
    apiVersions: ["v1"]
    resources: ["deployments", "statefulsets"]
  failurePolicy: Fail
  sideEffects: None

Validation logic (Go example):

// webhook-validator.go
func validateConfigReferences(pod *corev1.Pod) error {
    client := kubernetes.NewForConfigOrDie(config)
    
    for _, volume := range pod.Spec.Volumes {
        if volume.ConfigMap != nil {
            _, err := client.CoreV1().ConfigMaps(pod.Namespace).
                Get(context.TODO(), volume.ConfigMap.Name, metav1.GetOptions{})
            if err != nil {
                return fmt.Errorf("ConfigMap %s not found", volume.ConfigMap.Name)
            }
        }
        
        if volume.Secret != nil {
            _, err := client.CoreV1().Secrets(pod.Namespace).
                Get(context.TODO(), volume.Secret.SecretName, metav1.GetOptions{})
            if err != nil {
                return fmt.Errorf("Secret %s not found", volume.Secret.SecretName)
            }
        }
    }
    return nil
}

Quick Reference Commands

# Diagnose
kubectl get pods | grep CreateContainerConfigError
kubectl describe pod <pod-name> | grep -A 20 Events

# Verify resources
kubectl get configmap <name> -o yaml
kubectl get secret <name> -o jsonpath='{.data}' | jq 'keys'

# Check keys in ConfigMap
kubectl get configmap <name> -o json | jq '.data | keys'

# Check keys in Secret (base64 decoded)
kubectl get secret <name> -o json | jq -r '.data | to_entries[] | .key'

# Force pod recreation
kubectl delete pod <pod-name>
kubectl rollout restart deployment <deployment-name>

# Watch pod recovery
kubectl get pods -w

# Export pod spec for analysis
kubectl get pod <pod-name> -o yaml > debug-pod.yaml

Advanced Debugging Technique

#!/bin/bash
# deep-inspect.sh

POD=$1
NAMESPACE=${2:-default}

echo "=== Container Configuration Assembly ==="
kubectl get pod $POD -n $NAMESPACE -o json | jq '{
  configMaps: [
    .spec.volumes[]? | select(.configMap != null) | 
    {
      name: .configMap.name,
      mountedAs: .name,
      optional: .configMap.optional
    }
  ],
  secrets: [
    .spec.volumes[]? | select(.secret != null) |
    {
      name: .secret.secretName,
      mountedAs: .name,
      optional: .secret.optional
    }
  ],
  envFromConfigMaps: [
    .spec.containers[].envFrom[]? | select(.configMapRef != null) |
    .configMapRef.name
  ],
  envFromSecrets: [
    .spec.containers[].envFrom[]? | select(.secretRef != null) |
    .secretRef.name
  ],
  envConfigMapKeys: [
    .spec.containers[].env[]? | select(.valueFrom.configMapKeyRef != null) |
    {
      envVar: .name,
      configMap: .valueFrom.configMapKeyRef.name,
      key: .valueFrom.configMapKeyRef.key
    }
  ],
  envSecretKeys: [
    .spec.containers[].env[]? | select(.valueFrom.secretKeyRef != null) |
    {
      envVar: .name,
      secret: .valueFrom.secretKeyRef.name,
      key: .valueFrom.secretKeyRef.key
    }
  ]
}'

echo ""
echo "=== Cross-Reference Check ==="

# Check each referenced ConfigMap
kubectl get pod $POD -n $NAMESPACE -o json | \
  jq -r '.spec.volumes[]?.configMap.name // empty' | sort -u | \
  while read cm; do
    if kubectl get configmap $cm -n $NAMESPACE &>/dev/null; then
      echo "✅ ConfigMap: $cm"
    else
      echo "❌ ConfigMap: $cm (NOT FOUND)"
    fi
  done

# Check each referenced Secret
kubectl get pod $POD -n $NAMESPACE -o json | \
  jq -r '.spec.volumes[]?.secret.secretName // empty' | sort -u | \
  while read secret; do
    if kubectl get secret $secret -n $NAMESPACE &>/dev/null; then
      echo "✅ Secret: $secret"
    else
      echo "❌ Secret: $secret (NOT FOUND)"
    fi
  done

Conclusion

CreateContainerConfigError is a pre-start configuration assembly failure, not an application bug. The container never executed, making traditional debugging methods ineffective.

Resolution checklist:

  1. ✅ Run kubectl describe pod <name> – never start with logs
  2. ✅ Verify ConfigMaps/Secrets exist: kubectl get configmap/secret
  3. ✅ Check key names match exactly
  4. ✅ Validate RBAC permissions for ServiceAccount
  5. ✅ Recreate pod after fixing: kubectl delete pod or kubectl rollout restart

Prevention:

  • Validate manifests before deployment (--dry-run=client)
  • Use Helm/Kustomize to manage dependencies
  • Implement admission controllers for automated validation
  • Create CI/CD checks for resource references

When this error appears, check describe output immediately—the Events section contains the exact cause. Fix the configuration synchronization issue, recreate the pod, and your containers will start successfully.

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top