Skip to content
daniel@cosenza:~/blog
SRE & DevOpsHow-To July 10, 2026 3 min read

How to Set Up Secrets Management with HashiCorp Vault

A complete walkthrough deploying Vault, storing a secret, and retrieving it from a Kubernetes pod dynamically — instead of secrets sitting as plain base64 in a Kubernetes Secret object.

Kubernetes’ own Secret objects are only base64-encoded, not encrypted, by default — genuinely sensitive credentials benefit from a dedicated secrets manager like Vault, which adds real encryption, access policies, and audit logging.

Step 1: deploy Vault

helm repo add hashicorp https://helm.releases.hashicorp.com
helm install vault hashicorp/vault -n vault --create-namespace

Step 2: initialize and unseal Vault

kubectl exec -n vault vault-0 -- vault operator init
kubectl exec -n vault vault-0 -- vault operator unseal <unseal-key-1>
kubectl exec -n vault vault-0 -- vault operator unseal <unseal-key-2>
kubectl exec -n vault vault-0 -- vault operator unseal <unseal-key-3>

Vault starts sealed — encrypted at rest with no way to decrypt anything until enough unseal keys (a threshold, typically 3 of 5 generated at init) are provided. Store these keys and the initial root token somewhere genuinely secure and separate from Vault itself — losing them means losing access to everything Vault protects.

Step 3: enable the Kubernetes authentication method

vault auth enable kubernetes
vault write auth/kubernetes/config \
    kubernetes_host="https://kubernetes.default.svc:443"

This lets pods authenticate to Vault using their own Kubernetes ServiceAccount token, rather than needing a separate Vault-specific credential distributed out of band.

Step 4: write a secret

vault kv put secret/myapp/database username="appuser" password="s3cr3t"

Step 5: create a policy scoping exactly what can be read

# myapp-policy.hcl
path "secret/data/myapp/*" {
  capabilities = ["read"]
}
vault policy write myapp-policy myapp-policy.hcl

Scoping the policy to exactly the path a specific application needs — not a blanket allow — limits the blast radius if that application’s own credentials are ever compromised.

Step 6: bind the policy to a Kubernetes ServiceAccount

vault write auth/kubernetes/role/myapp \
    bound_service_account_names=myapp-sa \
    bound_service_account_namespaces=default \
    policies=myapp-policy \
    ttl=1h

Step 7: retrieve the secret from within a pod

# from inside the pod, using its own ServiceAccount token:
VAULT_TOKEN=$(vault write -field=token auth/kubernetes/login role=myapp jwt=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token))
vault kv get -field=password secret/myapp/database

In practice, this is usually handled by the Vault Agent injector, which automates fetching secrets and writing them to a file inside the pod, rather than requiring application code to call Vault’s API directly.

Step 8: enable the Vault Agent injector for automatic secret injection

helm upgrade vault hashicorp/vault --set injector.enabled=true
annotations:
  vault.hashicorp.com/agent-inject: "true"
  vault.hashicorp.com/role: "myapp"
  vault.hashicorp.com/agent-inject-secret-db: "secret/myapp/database"

With these annotations on a pod spec, Vault’s injector adds a sidecar that authenticates and writes the secret to a local file the application container can read — no application code changes needed to integrate with Vault directly.

Why dynamic, scoped access beats a static Kubernetes Secret

A Kubernetes Secret is a static value, readable by anything with RBAC permission to view Secrets in that namespace, with no built-in expiration or fine-grained audit trail of who actually read it. Vault’s model — short-lived tokens, scoped policies per application, and a full audit log of every access — is a meaningfully stronger security posture for credentials that actually matter, at the cost of the additional operational complexity of running and unsealing Vault itself.