Managing secrets in Kubernetes using default Secret objects is, frankly, a security risk. They are only base64 encoded, not encrypted, meaning anyone with access to the namespace can practically read your DB passwords in plain text. In my experience building secure pipelines, moving to a dedicated secrets manager is the single biggest security win you can achieve.
If you’re wondering how to setup HashiCorp Vault on Kubernetes, you’ve come to the right place. I’ve deployed Vault across various environments—from small staging clusters to high-availability production setups—and I’ve learned that the ‘easy way’ usually leads to pain during the first scaling event. In this guide, I’ll show you the robust way to get it running using Helm.
Prerequisites
- A running Kubernetes cluster (v1.24+).
kubectlinstalled and configured to point to your cluster.- Helm 3.0+ installed on your local machine.
- Basic familiarity with Kubernetes namespaces and services.
Step 1: Adding the HashiCorp Helm Repository
The most reliable way to deploy Vault is via the official Helm chart. It handles the complex deployment logic and provides a standardized way to manage versions. First, let’s add the repository and update your local charts:
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
Step 2: Configuring the Value File
Running Vault in ‘standalone’ mode is great for learning, but for anything serious, you want Integrated Storage (Raft). I always recommend creating a values.yaml file instead of passing flags via the CLI to ensure your configuration is version-controlled.
Create a file named vault-values.yaml with the following configuration:
# vault-values.yaml
server:
# Use Raft integrated storage for better reliability
ha:
enabled: true
raft:
enabled: true
setNodeId: true
config: |
cluster_addr = "[[ raft.cluster_addr ]]"
retry_join {
leader_api_addr = "[[ raft.leader_api_addr ]]"
}
# Expose Vault via a LoadBalancer for initial setup
service:
type: LoadBalancer
As we move toward a production environment, you’ll likely need to consider scaling HashiCorp Vault in production to handle higher request volumes and ensure zero downtime.
Step 3: Deploying Vault
Now, let’s deploy Vault into its own dedicated namespace. This keeps your security tooling isolated from your application logic.
kubectl create namespace vault
helm install vault hashicorp/vault \
--namespace vault \
-f vault-values.yaml
You can verify the deployment by checking the pods. Note that the pods will be in a 0/1 Ready state—this is normal because Vault starts in a sealed state. It requires a set of unseal keys to decrypt its master key before it can serve requests.
Here is how your terminal should look once the pods are deployed, as shown in the image below:
Step 4: Initializing and Unsealing Vault
Since Vault is sealed, we need to initialize it. I’ll exec into the first pod to run the init command:
kubectl exec -it vault-0 -n vault -- vault operator init
CRITICAL: This command will output 5 Unseal Keys and an Initial Root Token. Save these in a password manager immediately. If you lose these, your data is gone forever. There is no ‘forgot password’ button in Vault.
To unseal the vault, run the following command three times, using a different unseal key each time:
kubectl exec -it vault-0 -n vault -- vault operator unseal [YOUR_UNSEAL_KEY]
Once the Sealed status changes to false, you can log in using the root token:
kubectl exec -it vault-0 -n vault -- vault login [YOUR_ROOT_TOKEN]
Step 5: Enabling the Kubernetes Auth Method
The real magic happens when you let your Kubernetes pods authenticate with Vault using their own Service Accounts. This eliminates the need to store a ‘Vault Token’ inside your app’s environment variables.
Run these commands inside the Vault pod to enable K8s auth:
# Enable the auth method
vault auth enable kubernetes
# Configure Vault to talk to the K8s API
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc:443"
If you’re debating whether this complexity is worth it compared to simpler cloud-native options, check out my deep dive on AWS Secrets Manager vs HashiCorp Vault to see which fits your stack better.
Pro Tips for Vault on K8s
- Use the Vault Agent Injector: Instead of writing Vault API calls in your code, use the Vault Helm chart’s injector. It uses a mutating admission webhook to mount secrets as files in
/vault/secrets/. - Auto-Unseal: In production, manually entering keys is a nightmare. Use a cloud KMS (AWS KMS, Azure Key Vault, or GCP KMS) to enable Auto-Unseal.
- Resource Limits: Vault is memory-intensive during heavy encryption tasks. Always set
resources.limits.memoryto at least 512Mi to avoid OOMKills.
Troubleshooting Common Issues
Vault Pods Stuck in Pending
This is usually a PVC (Persistent Volume Claim) issue. Ensure your cluster has a default StorageClass configured. If you’re on a bare-metal cluster, you might need to manually create the PVs.
Authentication Failures (403 Forbidden)
Double-check that the ServiceAccount used by your application pod is exactly the same one mapped in the Vault role. A common mistake is forgetting to specify the namespace in the Vault role configuration.
What’s Next?
Now that you’ve mastered how to setup HashiCorp Vault on Kubernetes, the next step is to move away from the Root Token. I recommend creating a custom policy for your apps and mapping it to a specific Kubernetes Service Account. This follows the principle of least privilege, ensuring that if one pod is compromised, the attacker can’t steal every secret in your organization.