🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Vault Secrets Not Injecting into Kubernetes Pod Fix (2026)

Vault Agent Injector not mounting secrets into your pod? Here's how to debug and fix Vault secret injection issues in Kubernetes step by step.

DevOpsBoysMay 7, 20264 min read
Share:Tweet

vault.hashicorp.com/agent-inject annotations set but secrets not appearing in your pod? Here's the systematic fix.


How Vault Injection Works (Quick Overview)

The Vault Agent Injector is a mutating admission webhook. When a pod is created with Vault annotations, the injector:

  1. Intercepts the pod creation request
  2. Injects a vault-agent-init init container and a vault-agent sidecar
  3. The init container authenticates to Vault using Kubernetes auth
  4. Secrets are written to a shared in-memory volume at /vault/secrets/

If any step fails, secrets don't appear.


Step 1: Check If the Injector Is Running

bash
# Find the injector pod
kubectl get pods -n vault -l component=webhook
 
# Or check for agent-injector
kubectl get pods -A | grep agent-injector
 
# Check logs
kubectl logs -n vault -l component=webhook --tail=50

If the injector pod is not running, no secrets will ever inject.


Step 2: Check Pod Annotations

The annotations must be on the pod template, not just the Deployment metadata:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    metadata:
      annotations:                              # ← HERE, inside template
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "my-app-role"
        vault.hashicorp.com/agent-inject-secret-config: "secret/data/my-app/config"
    spec:
      containers:
      ...

Common mistake: annotations on metadata at the top level instead of spec.template.metadata.


Step 3: Check Init Container Logs

bash
# Get pod name
kubectl get pods -n my-namespace
 
# Check init container logs
kubectl logs -n my-namespace my-app-pod -c vault-agent-init
 
# If pod is running, check sidecar logs
kubectl logs -n my-namespace my-app-pod -c vault-agent

The init container logs will tell you exactly why authentication or secret fetch failed.


Fix 1: Kubernetes Auth Not Configured

The most common error:

Error authenticating: error looking up token: 
Error making API request.
URL: PUT http://vault:8200/v1/auth/kubernetes/login
Code: 403. Errors:
* permission denied

Fix — configure Kubernetes auth in Vault:

bash
# Enable Kubernetes auth (if not enabled)
vault auth enable kubernetes
 
# Configure with cluster details
vault write auth/kubernetes/config \
  kubernetes_host="https://kubernetes.default.svc:443" \
  kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  token_reviewer_jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token
 
# Create a policy for your app
vault policy write my-app-policy - <<EOF
path "secret/data/my-app/*" {
  capabilities = ["read"]
}
EOF
 
# Create a role binding the K8s service account to the policy
vault write auth/kubernetes/role/my-app-role \
  bound_service_account_names=my-app-sa \
  bound_service_account_namespaces=my-namespace \
  policies=my-app-policy \
  ttl=1h

Fix 2: Service Account Mismatch

The pod's service account must match what's in the Vault role:

bash
# Check which service account the pod uses
kubectl get pod my-app-pod -n my-namespace -o jsonpath='{.spec.serviceAccountName}'
 
# Verify the Vault role allows this service account
vault read auth/kubernetes/role/my-app-role
# Look at: bound_service_account_names, bound_service_account_namespaces

If they don't match, update the Vault role or the pod's serviceAccountName.


Fix 3: Secret Path Is Wrong

Error reading secret: Error making API request.
URL: GET http://vault:8200/v1/secret/data/my-app/config
Code: 403 or 404

KV v2 secrets have data/ in the path:

yaml
# KV v2 (most common) — path includes 'data'
vault.hashicorp.com/agent-inject-secret-config: "secret/data/my-app/config"
 
# KV v1 — no 'data' in path
vault.hashicorp.com/agent-inject-secret-config: "secret/my-app/config"

Verify the path exists:

bash
vault kv get secret/my-app/config    # KV v2
vault read secret/my-app/config      # KV v1

Fix 4: Injector Webhook Not Targeting Your Namespace

The Vault injector may have namespace selectors that exclude your namespace:

bash
# Check webhook configuration
kubectl get mutatingwebhookconfiguration vault-agent-injector-cfg -o yaml | grep -A10 namespaceSelector

If your namespace lacks the required label:

bash
# Add the label
kubectl label namespace my-namespace vault-injection=enabled
 
# Or check what label the webhook requires and add it

Fix 5: Secret Template Formatting

If secrets appear but are malformatted, check your template:

yaml
annotations:
  vault.hashicorp.com/agent-inject-secret-config: "secret/data/my-app/config"
  vault.hashicorp.com/agent-inject-template-config: |
    {{- with secret "secret/data/my-app/config" -}}
    DB_HOST={{ .Data.data.db_host }}
    DB_PASS={{ .Data.data.db_password }}
    {{- end }}

Without a template, Vault writes JSON to the file. With a template, you control the format.

Access the secret in your app:

bash
# Inside the pod
cat /vault/secrets/config

Fix 6: Vault Is Sealed or Unreachable

bash
# Check Vault is unsealed and reachable from the pod namespace
kubectl run vault-test --image=curlimages/curl --restart=Never \
  -n my-namespace -- \
  curl -s http://vault.vault.svc.cluster.local:8200/v1/sys/health
 
# Expected: {"initialized":true,"sealed":false,...}

If Vault is sealed → unseal it. If unreachable → check Service/NetworkPolicy.


Full Annotation Example (Working)

yaml
spec:
  template:
    metadata:
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/agent-inject-status: "update"
        vault.hashicorp.com/role: "my-app-role"
        vault.hashicorp.com/agent-inject-secret-config: "secret/data/my-app/config"
        vault.hashicorp.com/agent-inject-template-config: |
          {{- with secret "secret/data/my-app/config" -}}
          export DB_HOST="{{ .Data.data.db_host }}"
          export DB_PASS="{{ .Data.data.db_password }}"
          {{- end }}
    spec:
      serviceAccountName: my-app-sa  # must match Vault role
      containers:
      - name: my-app
        image: my-app:latest
        command: ["/bin/sh", "-c"]
        args: ["source /vault/secrets/config && ./start.sh"]

Quick summary:

  • Init container failing → check init container logs
  • 403 permission denied → Vault Kubernetes auth not configured or service account mismatch
  • 404 not found → wrong secret path (check KV v1 vs v2)
  • Webhook not triggering → namespace label missing
  • Vault unreachable → check NetworkPolicy and Vault seal status
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments