HashiCorp Vault — Agent Injector Deep Dive

2 minute read

Advanced patterns for the Vault Agent Injector — multi-secret injection, JSON configs, dynamic secrets, and troubleshooting.


Multi-Secret Injection

metadata:
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "myapp"

    # Database credentials
    vault.hashicorp.com/agent-inject-secret-db.env: "secret/data/myapp/db"
    vault.hashicorp.com/agent-inject-template-db.env: |
      {{ with secret "secret/data/myapp/db" -}}
      DB_HOST={{ .Data.data.host }}
      DB_PORT={{ .Data.data.port }}
      DB_USER={{ .Data.data.username }}
      DB_PASS={{ .Data.data.password }}
      {{- end }}

    # Redis credentials
    vault.hashicorp.com/agent-inject-secret-redis.env: "secret/data/myapp/redis"
    vault.hashicorp.com/agent-inject-template-redis.env: |
      {{ with secret "secret/data/myapp/redis" -}}
      REDIS_URL=redis://:{{ .Data.data.password }}@{{ .Data.data.host }}:{{ .Data.data.port }}
      {{- end }}

    # TLS certificate
    vault.hashicorp.com/agent-inject-secret-tls.crt: "pki_int/issue/web-server"
    vault.hashicorp.com/agent-inject-template-tls.crt: |
      {{ with secret "pki_int/issue/web-server" "common_name=myapp.default.svc" "ttl=24h" -}}
      {{ .Data.certificate }}
      {{ .Data.issuing_ca }}
      {{- end }}

    vault.hashicorp.com/agent-inject-secret-tls.key: "pki_int/issue/web-server"
    vault.hashicorp.com/agent-inject-template-tls.key: |
      {{ with secret "pki_int/issue/web-server" "common_name=myapp.default.svc" "ttl=24h" -}}
      {{ .Data.private_key }}
      {{- end }}

Files rendered at:

  • /vault/secrets/db.env
  • /vault/secrets/redis.env
  • /vault/secrets/tls.crt
  • /vault/secrets/tls.key

JSON Configuration Template

vault.hashicorp.com/agent-inject-secret-config.json: "secret/data/myapp/config"
vault.hashicorp.com/agent-inject-template-config.json: |
  {{ with secret "secret/data/myapp/config" -}}
  {
    "database": {
      "host": "{{ .Data.data.db_host }}",
      "port": {{ .Data.data.db_port }},
      "username": "{{ .Data.data.db_user }}",
      "password": "{{ .Data.data.db_pass }}"
    },
    "redis": {
      "url": "{{ .Data.data.redis_url }}"
    },
    "log_level": "{{ .Data.data.log_level }}"
  }
  {{- end }}

Dynamic Database Credentials

annotations:
  vault.hashicorp.com/agent-inject: "true"
  vault.hashicorp.com/role: "myapp"
  vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/myapp-role"
  vault.hashicorp.com/agent-inject-template-db-creds: |
    {{ with secret "database/creds/myapp-role" -}}
    PGHOST=db.internal
    PGPORT=5432
    PGDATABASE=mydb
    PGUSER={{ .Data.username }}
    PGPASSWORD={{ .Data.password }}
    {{- end }}

The sidecar agent automatically renews the dynamic credential lease.


Resource Tuning

annotations:
  vault.hashicorp.com/agent-inject: "true"
  vault.hashicorp.com/role: "myapp"

  # Resource limits for the sidecar
  vault.hashicorp.com/agent-limits-cpu: "100m"
  vault.hashicorp.com/agent-limits-mem: "128Mi"
  vault.hashicorp.com/agent-requests-cpu: "50m"
  vault.hashicorp.com/agent-requests-mem: "64Mi"

  # Run agent as specific user
  vault.hashicorp.com/agent-run-as-user: "1000"
  vault.hashicorp.com/agent-run-as-group: "1000"

  # Vault connection settings
  vault.hashicorp.com/tls-skip-verify: "false"
  vault.hashicorp.com/ca-cert: "/vault/tls/ca.crt"

Init-Only vs Sidecar

# Init-only: fetch secrets once, no sidecar stays running
annotations:
  vault.hashicorp.com/agent-inject: "true"
  vault.hashicorp.com/agent-pre-populate-only: "true"
  vault.hashicorp.com/role: "myapp"
  vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/config"

Use init-only when:

  • Secrets don’t change during pod lifetime
  • You want minimal resource usage
  • No dynamic secrets (which need renewal)

Troubleshooting

Check Injector Logs

kubectl -n vault logs -l app.kubernetes.io/name=vault-agent-injector

Check Sidecar Logs

# List containers in the pod
kubectl get pod myapp-xyz -o jsonpath='{.spec.containers[*].name}'

# Check agent sidecar logs
kubectl logs myapp-xyz -c vault-agent

# Check init container logs
kubectl logs myapp-xyz -c vault-agent-init

Common Issues

Issue Cause Fix
Pod stuck in Init Agent can’t authenticate Check SA name matches role
permission denied Policy doesn’t match Verify policy paths
No sidecar injected Annotations missing or webhook not running Check injector pods
Secret file empty Template error Check agent logs
service account not allowed SA/namespace mismatch Check role bindings

Verify Injector Webhook

# Check mutating webhook configuration
kubectl get mutatingwebhookconfigurations

# Verify the injector is running
kubectl -n vault get pods -l app.kubernetes.io/name=vault-agent-injector

Quick Reference

Annotation Purpose
agent-inject: "true" Enable injection
agent-pre-populate-only: "true" Init-only, no sidecar
agent-inject-secret-NAME: "PATH" Inject secret as file
agent-inject-template-NAME: "TPL" Custom template
agent-limits-cpu/mem Sidecar resources

Next: CSI Provider