← Back to list

Vault Agent Injector: Sidecar Secrets in Kubernetes

How Vault injects, renders, and refreshes secrets in pods without restarting your app

Kirshi Yin in Curious Devs Corner · 2026-02-17 16:26 · 84 claps · 5.4 min read paywalled
#devops #kubernetes #cloud-computing #cybersecurity #technology
Open on Medium ↗
Wiki topics: AGT · AI Agents ☁️ · DevOps & Cloud 🔒 · Cybersecurity

Vault Agent Injector: Sidecar Secrets in Kubernetes

How Vault injects, renders, and refreshes secrets in pods without restarting your app

Kubernetes and Vault logos on a computer screen. Image by the author.

Kubernetes and Vault logos on a computer screen. Image by the author.

The Vault Agent Injector is a Kubernetes admission webhook that modifies pods at creation time and adds Vault Agent init and sidecar containers. This allows applications to read secrets from files on disk instead of talking to Vault directly.

This is the fourth article from my Vault series. In the previous three articles, I covered:

If you are new to Vault, I recommend starting with those articles first. This one builds on top of them and assumes you already understand basic concepts like policies, authentication methods, and KV secrets.

In this article, we focus fully on the Vault Agent Injector using the sidecar pattern. The goal is not just to make it work, but to understand why each piece exists and what problem it solves.

Why Applications Should Not Talk to Vault Directly

Vault Agent is a helper process that runs next to your application. It handles a few critical tasks:

  • Authenticating to Vault
  • Fetching secrets
  • Renewing Vault tokens
  • Refreshing secrets when they change
  • Writing secrets to files

With Vault Agent in place, your application never calls Vault APIs. It does not manage tokens. It only reads files from disk.

This separation is important. When an application talks to Vault directly:

  • It must understand Vault APIs
  • It must store and renew tokens
  • It often needs a restart when secrets change

Vault Agent removes all of that logic from the app. Vault-related concerns stay outside the application boundary. The app stays focused on its own job.

What the Vault Agent Injector Does on Kubernetes

On Kubernetes, Vault Agent runs using the vault-k8s integration. This integration relies on a Mutating Admission Webhook.

The injector adds three things to the Pod:

  • An init container that runs once before the app starts
  • A Vault Agent sidecar container that runs next to the app
  • A shared volume where secrets are written

You don’t need to define these containers yourself. You only add annotations. The application container remains unchanged.

Here is the flow:

Vault Agent Diagram

Vault Agent Diagram

The Role of Init Container and Sidecar

Vault Agent supports two execution phases:

Init Container

The init container:

  • Runs before the application container starts
  • Authenticates to Vault
  • Fetches secrets
  • Writes them to disk
  • Exits

This guarantees that secrets exist before the app starts.

Sidecar Container

The sidecar:

  • Starts after the init container
  • Runs for the entire lifetime of the Pod
  • Renews the Vault token
  • Watches secrets
  • Re-renders files when values change

This is what allows secrets to change without restarting the application.

If the sidecar pattern or Kubernetes basics still feel unclear, I cover them in detail in my ebook [Master Kubernetes from Scratch](http://Master Kubernetes from Scratch), where I explain core Kubernetes concepts step by step.

Hands-on: Inject and Read Secrets with the Vault Agent

Step 1: Install Vault with the Injector Enabled

For this demo, we use Vault in dev mode. This is not for production, but it keeps the setup simple for testing purposes.

helm install vault hashicorp/vault \
  --namespace vault \
  --create-namespace \
  --set server.dev.enabled=true \
  --set injector.enabled=true

Step 2: Confirm the Injector Is Running

Verify the created resources:

kubectl get all -n vault

You should see:

  • vault-0 pod (Vault server)
  • vault-agent-injector deployment
  • Services for Vault and the injector

This confirms the injector is running.

Step 3: Allow Vault to Trust Kubernetes

Vault does not trust Kubernetes by default. We must explicitly configure this trust.

First, enable the Kubernetes auth method:

kubectl exec -n vault vault-0 -- vault auth enable kubernetes

Then configure it:

kubectl exec -n vault -i vault-0 -- vault write auth/kubernetes/config \
  token_reviewer_jwt="$(kubectl exec -n vault vault-0 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  kubernetes_host="https://kubernetes.default.svc:443" \
  kubernetes_ca_cert="$(kubectl exec -n vault vault-0 -- cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt)"

This setup allows Vault to:

  • Verify Service Account tokens
  • Confirm pod identity using the Kubernetes API

Without this step, authentication will fail later.

Step 4: Store a Test Secret in Vault

kubectl exec -n vault vault-0 -- vault kv put secret/my-app/config \
  username="demo-user" \
  password="demo-password"

This creates a KV v2 secret in Vault.

Verify it:

kubectl exec -n vault vault-0 -- vault kv get secret/my-app/config

This secret doesn’t exist as a Kubernetes Secret because Vault manages it.

Step 5: Limit Access with Policies

kubectl exec -n vault -i vault-0 -- vault policy write my-app - <<EOF
path "secret/data/my-app/*" {
  capabilities = ["read"]
}
EOF

This policy allows read access only to this app’s secrets.

Step 6: Bind Kubernetes Identity to Vault Access

kubectl exec -n vault vault-0 -- vault write auth/kubernetes/role/my-app \
  bound_service_account_names=my-app \
  bound_service_account_namespaces=app \
  policies=my-app \
  ttl=1h

This role binds together:

  • A namespace
  • A Service Account
  • A Vault policy

Only pods that match all three can authenticate and read secrets.

Step 7: Create the Application Namespace and Service Account

kubectl create ns app
kubectl create sa my-app -n app

These values must match the role configuration exactly.

Step 8: Tell the Injector What to Inject

apiVersion: v1
kind: Pod
metadata:
  name: my-app
  namespace: app
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "my-app"
    vault.hashicorp.com/agent-inject-secret-database-config: "secret/data/my-app/config"
    vault.hashicorp.com/agent-inject-template-database-config: |
      {{- with secret "secret/data/my-app/config" -}}
      export DB_USERNAME="{{ .Data.data.username }}"
      export DB_PASSWORD="{{ .Data.data.password }}"
      {{- end -}}
spec:
  serviceAccountName: my-app
  containers:
  - name: my-app
    image: nginx:latest
    command: ["/bin/sh"]
    args: ["-c", "while true; do sleep 30; done"]

These annotations act as a contract between your Pod and the injector.

  • agent-inject: true enables injection
  • role selects the Vault role
  • agent-inject-secret-* defines the Vault path and output file name
  • agent-inject-template-* defines how the secret is rendered

The file name comes from the annotation suffix. Multiple annotations create multiple files.

How Vault Agent Templates Work

Templates run inside the Vault Agent, not inside your application.

They:

  • Read data from Vault
  • Render it into files
  • Re-render when the data changes

In this example:

  • Values are mapped to environment variable exports
  • The file is written to /vault/secrets/database-config

Your application can source the file or read it directly.

Step 9: Verify Injection

kubectl get pods -n app

You should see 2/2 containers running.

After injection, the Pod contains:

  • vault-agent-init
  • vault-agent
  • A shared secrets volume

Step 10: Inspect Vault Agent Logs

Init container logs:

kubectl logs my-app -c vault-agent-init -n app

Sidecar logs:

kubectl logs my-app -c vault-agent -n app

These logs show authentication, token renewal, and template rendering.

Step 11: Read the Injected Secret

kubectl exec -it -n app my-app -c my-app -- cat /vault/secrets/database-config

You should see:

export DB_USERNAME="demo-user"
export DB_PASSWORD="demo-password"

Step 12: Update Secrets Without Restarting the App

Update the secret:

kubectl exec -n vault vault-0 -- vault kv put secret/my-app/config \
  username="demo-user" \
  password="demo-password2"

Vault creates a new version of the secret. The application keeps running.

How the Sidecar Detects and Applies Changes

The Vault Agent sidecar:

  • Renews the Vault token
  • Checks for secret updates
  • Re-renders templates

For KV v2 secrets, the default render interval is 5 minutes.

You can change it using:

template_config {
  static_secret_render_interval = "10m"
}

After the interval:

kubectl exec -it -n app my-app -c my-app -- cat /vault/secrets/database-config

The updated value appears without restarting the Pod (password=”demo-password2").

Sidecar logs confirm this:

agent.auth.handler: renewed auth token
agent: rendered "(dynamic)" => "/vault/secrets/database-config"

Conclusion

In this article, you learned that with the Vault Agent Injector and the sidecar pattern:

  • Applications never talk to Vault directly
  • Secrets live outside the app lifecycle
  • Token renewal happens automatically
  • Secret updates do not require restarts
  • Access stays limited by namespace, Service Account, and policy

I hope these Vault series have been useful! Thanks for reading!


메타데이터
post_id
f0ae983542f7
slug
vault-agent-injector-sidecar-secrets-in-kubernetes-f0ae983542f7
url
https://medium.com/curious-devs-corner/vault-agent-injector-sidecar-secrets-in-kubernetes-f0ae983542f7
canonical_url
https://medium.com/curious-devs-corner/vault-agent-injector-sidecar-secrets-in-kubernetes-f0ae983542f7
author_url
https://medium.com/@kirshiyin
status
ok
fetched_at
2026-06-14 11:28:49