HashiCorp Vault — Agent Injector
The Vault Agent Injector is a Kubernetes mutating webhook that automatically injects Vault Agent sidecars into pods based on annotations — no manual sidecar configuration needed.
How It Works
1. Pod created with vault annotations
2. Kubernetes sends admission request to Injector webhook
3. Injector mutates the pod spec:
- Adds init container (fetches initial secrets)
- Adds sidecar container (keeps secrets updated)
- Adds shared volume (tmpfs)
4. Pod starts with secrets available at /vault/secrets/
Install Agent Injector
# Add HashiCorp Helm repo
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
# Install Vault with injector enabled (injector is on by default)
helm install vault hashicorp/vault \
--set "injector.enabled=true" \
--set "server.enabled=false" \
--set "injector.externalVaultAddr=http://external-vault:8200"
If Vault is already running in the cluster:
helm install vault hashicorp/vault \
--set "injector.enabled=true"
Basic Annotation Usage
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
annotations:
# Enable injection
vault.hashicorp.com/agent-inject: "true"
# Vault role for authentication
vault.hashicorp.com/role: "myapp"
# Secret to inject
vault.hashicorp.com/agent-inject-secret-db-creds: "secret/data/myapp/db"
spec:
serviceAccountName: myapp
containers:
- name: myapp
image: myapp:latest
This injects the raw JSON secret at /vault/secrets/db-creds.
Custom Templates via Annotations
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
# Secret file 1: database credentials as env file
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 }}
# Secret file 2: API key
vault.hashicorp.com/agent-inject-secret-api-key: "secret/data/myapp/api"
vault.hashicorp.com/agent-inject-template-api-key: |
{{ with secret "secret/data/myapp/api" -}}
{{ .Data.data.key }}
{{- end }}
Annotation Reference
| Annotation | Description |
|---|---|
vault.hashicorp.com/agent-inject |
Enable injection ("true") |
vault.hashicorp.com/role |
Vault auth role |
vault.hashicorp.com/agent-inject-secret-NAME |
Secret path to inject as NAME |
vault.hashicorp.com/agent-inject-template-NAME |
Custom template for NAME |
vault.hashicorp.com/agent-inject-status |
Set to "update" to re-inject |
vault.hashicorp.com/agent-pre-populate |
Pre-populate secrets before app starts |
vault.hashicorp.com/agent-pre-populate-only |
Only inject once (no sidecar) |
vault.hashicorp.com/agent-inject-token |
Inject the Vault token file |
vault.hashicorp.com/agent-run-as-user |
UID for the agent containers |
vault.hashicorp.com/agent-limits-cpu |
Agent CPU limit |
vault.hashicorp.com/agent-limits-mem |
Agent memory limit |
vault.hashicorp.com/agent-requests-cpu |
Agent CPU request |
vault.hashicorp.com/agent-requests-mem |
Agent memory request |
vault.hashicorp.com/tls-skip-verify |
Skip TLS verification |
Dynamic Secrets with Injector
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
# Dynamic PostgreSQL credentials
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" -}}
postgresql://{{ .Data.username }}:{{ .Data.password }}@db.internal:5432/mydb
{{- end }}
The agent automatically renews the dynamic credential lease.
One-Time Injection (No Sidecar)
For secrets that don’t change:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/config"
This runs only the init container — no sidecar stays running. Uses fewer resources but won’t auto-rotate secrets.
Vault Setup for Injector
# 1. Enable Kubernetes auth
vault auth enable kubernetes
# 2. Configure it
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc"
# 3. Create a policy
vault policy write myapp - <<EOF
path "secret/data/myapp/*" {
capabilities = ["read"]
}
path "database/creds/myapp-role" {
capabilities = ["read"]
}
EOF
# 4. Create a role bound to the service account
vault write auth/kubernetes/role/myapp \
bound_service_account_names=myapp \
bound_service_account_namespaces=default \
policies=myapp \
ttl=1h
Quick Reference
| Task | Annotation |
|---|---|
| Enable injection | vault.hashicorp.com/agent-inject: "true" |
| Set auth role | vault.hashicorp.com/role: "myapp" |
| Inject a secret | vault.hashicorp.com/agent-inject-secret-NAME: "PATH" |
| Custom template | vault.hashicorp.com/agent-inject-template-NAME: "..." |
| One-shot (no sidecar) | vault.hashicorp.com/agent-pre-populate-only: "true" |
Next: Practice Exercises