Safeguard
Cryptography

How to set up HashiCorp Vault for secrets management

A step-by-step guide to set up HashiCorp Vault for secrets management, covering installation, dynamic secrets, Kubernetes integration, and verification steps.

Karan Patel
Cloud Security Engineer
7 min read

Storing API keys, database passwords, and TLS certificates in .env files or CI/CD variables feels convenient until one of those files ends up in a git commit, a Slack message, or a compromised build log. That's the scenario HashiCorp Vault exists to prevent. This vault secrets management tutorial walks you through how to set up HashiCorp Vault from a clean install to a production-leaning configuration: initializing and unsealing the server, enabling authentication and policies, storing static secrets, generating short-lived dynamic database credentials, and wiring Vault into a Kubernetes cluster so pods never see a plaintext secret at rest.

By the end, you'll have a running Vault instance, a working access policy, at least one secrets engine populated with real data, and a verification checklist to confirm everything is actually locked down — not just running. We'll also cover the failure modes teams hit most often (sealed vaults after restart, permission denied errors, expired tokens) and how Safeguard fits into the broader picture of securing the secrets your software supply chain depends on.

Step 1: Install and Start HashiCorp Vault

Vault ships as a single binary, which makes it easy to run locally, in a container, or on a hardened VM. On macOS or Linux with Homebrew:

brew tap hashicorp/tap
brew install hashicorp/tap/vault
vault --version

On Debian/Ubuntu:

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

For a quick local evaluation, start Vault in dev mode, which auto-unseals and stores data in memory:

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

For anything beyond a demo, run Vault in server mode with a real storage backend (Consul, Integrated Storage/Raft, or a cloud KMS-backed setup) and a config file like this:

# vault-config.hcl
storage "raft" {
  path    = "/opt/vault/data"
  node_id = "vault-node-1"
}

listener "tcp" {
  address     = "0.0.0.0:8200"
  tls_cert_file = "/etc/vault/tls/vault.crt"
  tls_key_file  = "/etc/vault/tls/vault.key"
}

api_addr = "https://127.0.0.1:8200"
cluster_addr = "https://127.0.0.1:8201"
ui = true
vault server -config=vault-config.hcl

Step 2: Initialize and Unseal Vault

A freshly started Vault server is sealed by default — it knows where its data is but can't read it until it has enough unseal keys. Initialize it once:

export VAULT_ADDR="https://127.0.0.1:8200"
vault operator init -key-shares=5 -key-threshold=3

This returns five unseal key shares and a root token. Store them somewhere other than a text file on your laptop — a hardware security module, a secure password manager with restricted access, or split among trusted operators. Unseal the server with any three of the five keys:

vault operator unseal <unseal_key_1>
vault operator unseal <unseal_key_2>
vault operator unseal <unseal_key_3>

Log in with the root token to confirm access, then plan to replace it with scoped tokens immediately — the root token should be used only for initial setup, not day-to-day operations:

vault login <root_token>
vault status

Step 3: Set Up HashiCorp Vault Authentication and Policies

The core of any secure setup is deciding who can read what. Rather than distributing tokens by hand, enable an auth method that maps to your existing identity provider. For humans, userpass or OIDC works well; for services, AppRole or Kubernetes auth is the standard choice.

vault auth enable approle
vault auth enable userpass

Next, write a policy that grants least-privilege access. For example, a policy for an application that only needs to read secrets under secret/data/payments-api:

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

Then create an AppRole tied to that policy:

vault write auth/approle/role/payments-api \
  token_policies="payments-policy" \
  token_ttl=1h \
  token_max_ttl=4h

Every credential Vault issues from here on inherits this scoping, which is the difference between "Vault has secrets" and "Vault actually reduces blast radius."

Step 4: Enable the KV Secrets Engine for Static Secrets

For credentials that don't rotate automatically — API keys from third-party SaaS tools, signing certificates, webhook secrets — use the versioned key-value engine:

vault secrets enable -path=secret kv-v2
vault kv put secret/payments-api/stripe api_key="sk_live_xxx" webhook_secret="whsec_xxx"
vault kv get secret/payments-api/stripe

Because kv-v2 is versioned, you can roll back a secret if it's overwritten by mistake:

vault kv get -version=1 secret/payments-api/stripe
vault kv rollback -version=1 secret/payments-api/stripe

Step 5: Configure Vault Dynamic Secrets for Databases

Static secrets are a starting point, but the real security upgrade comes from Vault dynamic secrets: credentials generated on demand, scoped to a short TTL, and automatically revoked. For a Postgres database:

vault secrets enable database

vault write database/config/payments-db \
  plugin_name=postgresql-database-plugin \
  connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/payments?sslmode=require" \
  allowed_roles="payments-role" \
  username="vault_admin" \
  password="admin_password"

vault write database/roles/payments-role \
  db_name=payments-db \
  creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
    GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
  default_ttl="1h" \
  max_ttl="24h"

Now, instead of an app holding a permanent database password, it requests one at runtime:

vault read database/creds/payments-role

Each call returns a unique username and password that expires automatically. If the credential leaks, its usefulness is measured in minutes, not months, and revoking it early is one command:

vault lease revoke database/creds/payments-role/<lease_id>

Step 6: Set Up Vault Kubernetes Integration

If your workloads run in Kubernetes, Vault Kubernetes integration lets pods authenticate using their service account tokens instead of injected static secrets. Enable and configure the Kubernetes auth method from inside (or with network access to) the cluster:

vault auth enable kubernetes

vault write auth/kubernetes/config \
  kubernetes_host="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT" \
  kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  token_reviewer_jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token

Bind a Kubernetes service account to the policy created earlier:

vault write auth/kubernetes/role/payments-api \
  bound_service_account_names=payments-api \
  bound_service_account_namespaces=payments \
  policies=payments-policy \
  ttl=1h

Most teams pair this with the Vault Agent Injector, which watches pod annotations and automatically sidecars a Vault agent that fetches and renews secrets, writing them to a shared volume the application container reads at startup:

annotations:
  vault.hashicorp.com/agent-inject: "true"
  vault.hashicorp.com/role: "payments-api"
  vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/payments-role"

Verifying Your Vault Setup and Troubleshooting Common Issues

Before calling the setup done, run through a short verification pass:

  • Confirm seal status after every restart: vault status should report Sealed: false. If Vault restarts and comes back sealed, your storage backend or auto-unseal (KMS/cloud) config needs review — this is the single most common outage cause in production Vault deployments.
  • Test policy boundaries, not just success paths: log in as the AppRole and confirm it cannot read paths outside its policy. A policy that's too broad is a silent failure that won't show up until an incident.
  • Check audit logging is enabled so every secret access is traceable: vault audit enable file file_path=/var/log/vault_audit.log.
  • Watch for permission denied errors, which usually mean either the token's policy doesn't cover the requested path, or the token has expired — check vault token lookup for TTL and renewal status.
  • Watch for lease expiration surprises with dynamic secrets — if an application caches a database credential past its TTL, connections will start failing; make sure your app or Vault Agent handles renewal (vault lease renew) proactively.
  • Validate Kubernetes auth end-to-end by exec'ing into a pod and confirming the injected secret file exists and contains a live, non-expired credential, not a stale one from an earlier deploy.

How Safeguard Helps

Vault solves secret storage and issuance, but it doesn't tell you whether a secret leaked upstream of Vault entirely — in a git history, a container image layer, a CI log, or a dependency's build script. Safeguard's software supply chain security platform scans repositories, pipelines, and artifacts for exposed credentials, misconfigured secrets engines, and drift between what's declared in policy and what's actually reachable at runtime, so a well-configured Vault instance isn't undermined by a leaked root token sitting in an old commit.

Safeguard also maps how secrets flow through your build and deployment pipeline — which services request which Vault paths, which CI jobs have standing access, and where dynamic secrets are being requested versus where static ones still linger — giving security teams a supply-chain-wide view that complements Vault's own audit log. If you're rolling out Vault across multiple teams, that visibility is what turns "we have Vault" into "we have provable least-privilege secrets management," which is increasingly what SOC 2 and customer security questionnaires actually ask for.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.