All Articles

How to Set Up HashiCorp Vault for Secrets Management from Scratch (2026)

HashiCorp Vault is the industry standard for secrets management. This step-by-step guide shows you how to install Vault, configure it, and integrate it with Kubernetes.

DevOpsBoysMar 14, 20266 min read
Share:Tweet

Hardcoded secrets in code, plain-text passwords in ConfigMaps, API keys in environment variables baked into Docker images — this is how breaches happen.

HashiCorp Vault solves this. It's the industry-standard secrets management platform that lets you:

  • Store secrets centrally and access them over an API
  • Rotate credentials automatically
  • Control exactly who and what can read which secret
  • Audit every single access to every secret

This guide walks you through setting up Vault from scratch — locally first, then in Kubernetes.


What Is HashiCorp Vault?

Vault is a secrets management tool that acts as a single source of truth for sensitive data: API keys, database passwords, TLS certificates, SSH keys, and more.

The core model is simple:

  1. You store a secret in Vault (e.g., secret/myapp/db-password)
  2. Vault authenticates any client that wants to read it (Kubernetes pod, CI runner, developer)
  3. Vault authorizes the access based on policies
  4. Vault returns the secret and logs the access

Everything is encrypted at rest, audited, and controlled.


Prerequisites

Before starting, you need:

  • Docker installed (for local setup)
  • kubectl + a Kubernetes cluster (for the K8s integration section)
  • Helm installed
  • The vault CLI installed

Install the Vault CLI on Linux/Mac:

bash
# Mac
brew tap hashicorp/tap
brew install hashicorp/tap/vault
 
# Ubuntu/Debian
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vault

Part 1: Run Vault Locally (Dev Mode)

Start Vault in development mode to explore without full setup:

bash
vault server -dev -dev-root-token-id="root"

In a second terminal, configure your environment:

bash
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='root'
 
# Verify Vault is running
vault status

You'll see:

Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.17.0
HA Enabled      false

Dev mode is not for production — secrets are in-memory and lost on restart. Use it to learn the API.


Part 2: Store and Read Your First Secret

Vault uses a filesystem-like path model. Secrets go under secret/ (KV v2 by default in dev mode).

Write a secret

bash
vault kv put secret/myapp/database \
  username="admin" \
  password="super-secret-password" \
  host="postgres.internal:5432"

Read it back

bash
vault kv get secret/myapp/database

Output:

======== Secret Path ========
secret/data/myapp/database

======= Metadata =======
Key              Value
---              -----
created_time     2026-03-14T10:00:00Z
current_version  1

====== Data ======
Key         Value
---         -----
host        postgres.internal:5432
password    super-secret-password
username    admin

Get a specific field

bash
vault kv get -field=password secret/myapp/database

Update a secret (creates a new version)

bash
vault kv put secret/myapp/database \
  username="admin" \
  password="new-rotated-password" \
  host="postgres.internal:5432"

View version history

bash
vault kv metadata get secret/myapp/database
 
# Read a specific version
vault kv get -version=1 secret/myapp/database

Part 3: Policies — Controlling Access

Vault uses policies to control who can read or write which paths.

Create a read-only policy for your app

bash
cat > myapp-policy.hcl << 'EOF'
path "secret/data/myapp/*" {
  capabilities = ["read"]
}
 
path "secret/metadata/myapp/*" {
  capabilities = ["list"]
}
EOF
 
vault policy write myapp-readonly myapp-policy.hcl

Verify the policy

bash
vault policy read myapp-readonly

An admin policy that can manage secrets:

hcl
# admin-policy.hcl
path "secret/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}
 
path "auth/*" {
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
 
path "sys/policies/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

Part 4: Production Setup with TLS and Persistent Storage

For production, you need Vault backed by real storage and TLS.

Production config file

hcl
# config.hcl
ui = true
 
storage "raft" {
  path    = "/vault/data"
  node_id = "node1"
}
 
listener "tcp" {
  address       = "0.0.0.0:8200"
  tls_cert_file = "/vault/tls/tls.crt"
  tls_key_file  = "/vault/tls/tls.key"
}
 
api_addr     = "https://vault.yourdomain.com:8200"
cluster_addr = "https://vault.yourdomain.com:8201"

Start Vault with this config

bash
vault server -config=config.hcl

Initialize Vault (first time only)

bash
# Initialize with 5 key shares, 3 required to unseal
vault operator init -key-shares=5 -key-threshold=3

This outputs 5 unseal keys and a root token. Save these securely — you cannot recover them.

Unseal Vault

After every restart, Vault is sealed and needs 3 of 5 keys:

bash
vault operator unseal <key1>
vault operator unseal <key2>
vault operator unseal <key3>

Part 5: Deploy Vault in Kubernetes

For Kubernetes, use the official Helm chart:

bash
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update

Create a production-ready values file

yaml
# vault-values.yaml
server:
  ha:
    enabled: true
    replicas: 3
    raft:
      enabled: true
      setNodeId: true
 
  resources:
    requests:
      memory: 256Mi
      cpu: 250m
    limits:
      memory: 512Mi
      cpu: 500m
 
  dataStorage:
    enabled: true
    size: 10Gi
    storageClass: "gp2"
 
  auditStorage:
    enabled: true
    size: 10Gi
 
  ingress:
    enabled: true
    hosts:
      - host: vault.yourdomain.com
        paths: ["/"]
    tls:
      - secretName: vault-tls
        hosts:
          - vault.yourdomain.com
 
ui:
  enabled: true
 
injector:
  enabled: true    # enables the Agent Injector for sidecar injection
bash
helm install vault hashicorp/vault \
  --namespace vault \
  --create-namespace \
  -f vault-values.yaml

Part 6: Kubernetes Authentication

The Vault Agent Injector lets Kubernetes pods get secrets injected automatically — no code changes required.

Enable Kubernetes auth in Vault

bash
# Enable the Kubernetes auth method
vault auth enable kubernetes
 
# Configure it with your cluster info
vault write auth/kubernetes/config \
  kubernetes_host="https://kubernetes.default.svc:443" \
  kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  token_reviewer_jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token

Create a role that maps K8s service accounts to Vault policies

bash
vault write auth/kubernetes/role/myapp \
  bound_service_account_names="myapp-sa" \
  bound_service_account_namespaces="production" \
  policies="myapp-readonly" \
  ttl="1h"

Annotate your Pod to get secrets injected

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: production
spec:
  template:
    metadata:
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "myapp"
        vault.hashicorp.com/agent-inject-secret-database.txt: "secret/data/myapp/database"
        vault.hashicorp.com/agent-inject-template-database.txt: |
          {{- with secret "secret/data/myapp/database" -}}
          DB_USERNAME={{ .Data.data.username }}
          DB_PASSWORD={{ .Data.data.password }}
          DB_HOST={{ .Data.data.host }}
          {{- end -}}
    spec:
      serviceAccountName: myapp-sa
      containers:
        - name: myapp
          image: myapp:latest
          env:
            - name: DB_CREDENTIALS_FILE
              value: /vault/secrets/database.txt

The Vault Agent sidecar will inject the file /vault/secrets/database.txt with the rendered template. Your app reads the file — no secrets in env vars, no Kubernetes Secrets, no plaintext anywhere.


Part 7: Dynamic Database Credentials (Advanced)

Vault can generate temporary database credentials that auto-expire:

bash
# Enable the database secrets engine
vault secrets enable database
 
# Configure a PostgreSQL connection
vault write database/config/mydb \
  plugin_name=postgresql-database-plugin \
  allowed_roles="myapp-role" \
  connection_url="postgresql://{{username}}:{{password}}@postgres:5432/mydb" \
  username="vault-admin" \
  password="vault-admin-password"
 
# Create a role that generates credentials
vault write database/roles/myapp-role \
  db_name=mydb \
  creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
  default_ttl="1h" \
  max_ttl="24h"

Now every time an app requests credentials, it gets a unique, time-limited username/password:

bash
vault read database/creds/myapp-role
Key                Value
---                -----
username           v-myapp-abc123
password           A1b2-C3d4-E5f6-G7h8
lease_duration     1h

After 1 hour, those credentials automatically expire and are deleted from PostgreSQL. Even if leaked, they're useless within an hour.


Vault CLI Quick Reference

bash
# Check status
vault status
 
# List secrets at a path
vault kv list secret/
 
# Write a secret
vault kv put secret/myapp key=value
 
# Read a secret
vault kv get secret/myapp
 
# Delete a secret
vault kv delete secret/myapp
 
# List auth methods
vault auth list
 
# List secrets engines
vault secrets list
 
# Check token info
vault token lookup

Learn More

Want hands-on Vault labs in a real Kubernetes environment? KodeKloud's DevOps courses include dedicated Vault content with real cluster exercises. You'll go from zero to production-ready Vault in a structured path.

If you need a cloud environment to practice, DigitalOcean's managed Kubernetes is one of the most affordable ways to run a real cluster without the complexity of EKS or GKE.


Summary

HashiCorp Vault is the right answer to secrets management at any scale:

  1. Dev mode — learn locally in minutes
  2. KV store — simple key-value secrets with versioning
  3. Policies — fine-grained control over who reads what
  4. Kubernetes integration — Agent Injector for zero-code-change secret injection
  5. Dynamic credentials — auto-expiring database credentials for maximum security

Start by moving your database passwords out of Kubernetes Secrets and into Vault. That single change dramatically reduces your blast radius if any service is ever compromised.

Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments