How to Implement Secrets Management with HashiCorp Vault

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
The alternative to Vault is secrets scattered everywhere: database passwords in application.properties files, API keys in CI/CD environment variables shared across the team, TLS certificates on developer laptops, and cloud credentials in ~/.aws/credentials. Any one of these locations can leak, and there is no audit trail to know which secrets were accessed by whom and when.
Vault centralizes all of this behind a single authenticated API with fine-grained policies and a complete audit log. Applications prove their identity (via AppRole, Kubernetes service account, or AWS IAM role), receive only the credentials their policy allows, and every access is logged.
Installation and Initial Setup
Install Vault (Linux):
# Add HashiCorp GPG key and repository
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list
apt update && apt install vault
# Start Vault in dev mode for local testing (NOT production: dev mode is unsealed and in-memory)
vault server -dev -dev-root-token-id="root"
# Set environment variables
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='root'
# Verify
vault status
Production setup: basic config file:
# /etc/vault.d/vault.hcl
storage "raft" {
path = "/opt/vault/data"
node_id = "vault-node-1"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/opt/vault/tls/vault.crt"
tls_key_file = "/opt/vault/tls/vault.key"
}
api_addr = "https://vault.internal.company.com:8200"
cluster_addr = "https://vault-node-1.internal:8201"
ui = true
Initialize and unseal (production):
# Initialize Vault: generates unseal keys and root token
vault operator init -key-shares=5 -key-threshold=3
# CRITICAL: Save the unseal keys and root token securely: if you lose them, data is unrecoverable
# Use Shamir's Secret Sharing: 5 keys generated, 3 required to unseal
# Distribute keys to 5 trusted people or store in separate secure locations
# Unseal (requires 3 of 5 key shares)
vault operator unseal <key-share-1>
vault operator unseal <key-share-2>
vault operator unseal <key-share-3>
KV Secrets Engine: Storing Static Secrets
KV-v2 (Key-Value version 2) is the primary secrets engine for storing static secrets. It maintains a version history and supports soft-delete and metadata.
# Enable KV-v2 secrets engine
vault secrets enable -path=secret kv-v2
# Write a secret
vault kv put secret/myapp/database \
host="db.internal.company.com" \
username="app_user" \
password="$(openssl rand -base64 32)"
# Read a secret
vault kv get secret/myapp/database
# Read just the password field
vault kv get -field=password secret/myapp/database
# Read a specific version
vault kv get -version=2 secret/myapp/database
# List all secrets at a path
vault kv list secret/myapp/
# Soft-delete a secret version
vault kv delete secret/myapp/database
# Read via API (for application integration)
curl -H "X-Vault-Token: $VAULT_TOKEN" \
$VAULT_ADDR/v1/secret/data/myapp/database
Organizing secrets by service and environment:
# Recommended path structure
secret/
prod/
webapp/
database
redis
stripe-api-key
staging/
webapp/
database
shared/
tls-certificates/
internal-ca
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Dynamic Database Secrets
Dynamic secrets are generated on-demand and automatically expired: the application gets a unique database username and password that did not exist before the request and will not exist after the lease expires.
# Enable the database secrets engine
vault secrets enable database
# Configure a PostgreSQL connection
vault write database/config/webapp-postgres \
plugin_name=postgresql-database-plugin \
allowed_roles="webapp-role" \
connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/appdb" \
username="vault-admin" \
password="$(cat /run/secrets/vault-db-admin-password)"
# Create a role: this defines what credentials look like when generated
vault write database/roles/webapp-role \
db_name=webapp-postgres \
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="5m" \
max_ttl="1h"
# Generate dynamic credentials (what your application calls)
vault read database/creds/webapp-role
# Returns: username=v-appid-webapp-abc123-1234567890, password=A1B2C3..., lease_duration=5m
The generated credentials exist only for 5 minutes (or until the lease is explicitly revoked). If the credentials leak, they expire automatically. There is no shared database password to rotate.
Renew a lease (if the application needs more time):
vault lease renew database/creds/webapp-role/<lease-id>
Revoke all credentials for a role immediately (incident response):
vault lease revoke -prefix database/creds/webapp-role
# Revokes all active leases: every application using these credentials loses access instantly
AppRole Auth and Policies
AppRole is the standard auth method for applications running outside Kubernetes. Applications exchange a Role ID (non-secret, like a username) and a Secret ID (secret, like a password) for a Vault token.
Enable AppRole and create an application role:
# Enable AppRole auth
vault auth enable approle
# Create a policy for the webapp (what it is allowed to read)
vault policy write webapp-policy - <<EOF
# Allow reading only the specific secrets this app needs
path "secret/data/prod/webapp/*" {
capabilities = ["read"]
}
# Allow generating dynamic database credentials
path "database/creds/webapp-role" {
capabilities = ["read"]
}
# Allow renewing leases
path "sys/leases/renew" {
capabilities = ["update"]
}
EOF
# Create an AppRole for the webapp
vault write auth/approle/role/webapp \
token_policies="webapp-policy" \
token_ttl=1h \
token_max_ttl=4h \
secret_id_ttl=10m \
secret_id_num_uses=1 # Secret ID is single-use: a new one must be requested each time
# Get the Role ID (non-secret: can be included in app config)
vault read auth/approle/role/webapp/role-id
# role_id: db02de05-fa39-4855-059b-67221c5c2f63
# Generate a Secret ID (inject this at deploy time via CI/CD, not stored with the app)
vault write -f auth/approle/role/webapp/secret-id
# secret_id: 6a174c20-f6de-a53c-74d2-6018fcceff64
# Application login (what the app does at startup)
vault write auth/approle/login \
role_id="$VAULT_ROLE_ID" \
secret_id="$VAULT_SECRET_ID"
# Returns a token the app uses for subsequent requests
Kubernetes auth method (for containerized workloads):
vault auth enable kubernetes
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
vault write auth/kubernetes/role/webapp \
bound_service_account_names=webapp \
bound_service_account_namespaces=production \
policies=webapp-policy \
ttl=1h
# Application running in Kubernetes authenticates with its service account token
# No Secret ID required: the Kubernetes identity IS the credential
vault write auth/kubernetes/login \
role=webapp \
jwt=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
The bottom line
Vault solves secrets sprawl by centralizing credentials behind an authenticated API with fine-grained policies and complete audit logging. Start with KV-v2 for existing static secrets, then migrate to dynamic secrets for database credentials: per-request, auto-expiring credentials eliminate the shared-password rotation problem. Use AppRole for VM-based applications, Kubernetes auth for containerized workloads. The audit log that records every secret access is often as valuable as the secrets management itself.
Frequently asked questions
What is HashiCorp Vault used for?
Vault centralizes secrets management: storing API keys, database passwords, TLS certificates, and cloud credentials in one place with authenticated access, fine-grained policies, and complete audit logging. It also supports dynamic secrets: database credentials generated on-demand with a short TTL (like 5 minutes), so leaked credentials expire automatically rather than needing manual rotation.
How do applications authenticate to HashiCorp Vault?
Applications use auth methods matched to their deployment environment: AppRole (Role ID + one-time Secret ID) for VM-based applications, Kubernetes service account tokens for containerized workloads, AWS IAM roles for EC2/ECS/Lambda, and OIDC for human users. Each method issues a short-lived Vault token that the application uses for subsequent API requests.
What is HashiCorp Vault and what problem does it solve?
HashiCorp Vault is a secrets management platform that centralizes storage, access control, and auditing of secrets: API keys, database credentials, TLS certificates, and SSH keys. Without Vault, secrets are typically stored in environment variables, config files, or CI/CD secret stores — scattered across systems with no centralized audit log of who accessed what. Vault provides: a single API for secret access across all environments; fine-grained access policies (which applications can access which secrets, with optional MFA and time-bound approval); complete audit logs of every secret access; and dynamic secrets (credentials generated on-demand and auto-expired) for databases and cloud providers, eliminating long-lived static credentials entirely.
What are HashiCorp Vault dynamic secrets and how do they work?
Dynamic secrets are credentials generated by Vault on demand and automatically revoked after a configurable lease period. For databases (PostgreSQL, MySQL, MongoDB): Vault creates a new database user with the requested permissions when an application requests credentials, returns the username and password, and drops the user when the lease expires (e.g., 1 hour). For AWS: Vault generates an IAM user or assumes a role and returns temporary access keys. This eliminates long-lived credentials entirely: if the credentials are stolen, they expire within the lease window. Dynamic secrets are the highest-value Vault feature for eliminating credential theft risk.
What is the difference between HashiCorp Vault and AWS Secrets Manager?
Both are secrets management platforms, but they differ in scope. AWS Secrets Manager is cloud-native to AWS: it manages secrets with tight AWS IAM integration, automatic rotation for RDS databases, and simple deployment — but it only works in AWS. HashiCorp Vault is multi-cloud and on-premises: it supports secrets management across AWS, Azure, GCP, on-premises systems, and Kubernetes, making it the choice for multi-cloud or hybrid environments. AWS Secrets Manager is the right choice for AWS-only environments that want minimal operational overhead. Vault is the right choice when you need a single secrets management platform across heterogeneous infrastructure or require dynamic secrets for non-AWS resources.
How do you handle Vault's unseal keys securely in a production deployment?
Vault's unseal keys require careful operational handling because losing them makes encrypted data permanently unrecoverable, and exposing them breaks the security model. Vault uses Shamir's Secret Sharing to split the master key: you specify how many key shares to generate (recommended: 5) and how many are required to unseal (recommended: 3). This means no single person holds enough key material to unseal Vault alone. Distribute the five key shares to five different trusted individuals, with each person storing their share in a separate secure location (a password manager they control, a physical safe, or an HSM). Never store all shares in the same system or with the same person. For production environments, use Vault's auto-unseal feature backed by a cloud KMS (AWS KMS, Azure Key Vault, or GCP Cloud KMS): this removes the need for manual key entry on startup and eliminates the human-process dependency, while the cloud KMS handles key custody. The root token generated during initialization should be used once to configure Vault, then revoked immediately: admin access should use individual Vault user accounts with appropriate policies, not the omnipotent root token. Document the unsealing procedure and distribute it separately from the key shares so the process can be executed by the key holders even during an emergency.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
