Secrets Management with HashiCorp Vault

Khimananda Oli 5 min read Database
Secrets Management with HashiCorp Vault

By Khimananda Oli | Last reviewed: August 2026

Hardcoded credentials in source control remain the leading cause of preventable breaches, yet many teams still rely on environment variables or encrypted files that lack audit trails and rotation capabilities. Proper secrets management with HashiCorp Vault solves this by centralizing encryption, enforcing access policies, and generating short-lived credentials dynamically. If you are building cloud-native applications or preparing for SOC 2 compliance, moving beyond static secrets is no longer optional.

App / ServiceVault ServerAuth + PoliciesAWS / CloudPostgreSQL DBPKI / Certs
Core architecture for secrets management with HashiCorp Vault connecting applications to dynamic backend providers

How do you configure secrets management with HashiCorp Vault for production?

Setting up Vault requires treating it as critical infrastructure, not an afterthought. In my experience helping Nepali fintech startups achieve ISO 27001 certification, the difference between a toy setup and a production-grade deployment lies in storage backend selection, high availability configuration, and unseal key management. Never run Vault with in-memory storage outside of local development.

Storage Backend and High Availability

For production, use Integrated Storage (Raft) or Consul. Raft is now the recommended default for most deployments because it eliminates external dependencies while providing strong consistency. Configure at least three nodes to tolerate a single failure without losing quorum.

# vault-config.hcl (Production Raft Example)
ui = true
disable_mlock = false

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

storage "raft" {
  path    = "/opt/vault/data"
  node_id = "vault-node-1"

  retry_join {
    leader_api_addr = "https://vault-2.internal:8200"
  }
  retry_join {
    leader_api_addr = "https://vault-3.internal:8200"
  }
}

seal "awskms" {
  region     = "ap-south-1"
  kms_key_id = "alias/vault-unseal-key"
}

Always enable auto-unseal using a cloud KMS (AWS KMS, Azure Key Vault, or GCP Cloud KMS). Manual unseal with Shamir shares is operationally fragile; if your team cannot respond to a restart at 3 AM during a monsoon-induced power outage in Kathmandu, your service will remain down until someone physically intervenes. Auto-unseal removes this human bottleneck securely.

Initial Policy and Auth Method Setup

After initialization, enable only the auth methods your workloads actually need. For Kubernetes-native applications, use the Kubernetes auth method. For CI/CD pipelines integrating with tools discussed in our GitLab CI pipeline guide, use AppRole or JWT/OIDC authentication.

# Enable Kubernetes auth
vault auth enable kubernetes

# Configure Kubernetes connection
vault write auth/kubernetes/config \
  kubernetes_host="https://kubernetes.default.svc:443"

# Create a read-only policy for app secrets
vault policy write app-read - <<EOF
path "secret/data/apps/myapp/*" {
  capabilities = ["read"]
}
path "database/creds/app-role" {
  capabilities = ["read"]
}
EOF

# Bind policy to Kubernetes service account
vault write auth/kubernetes/role/myapp \
  bound_service_account_names=myapp-sa \
  bound_service_account_namespaces=production \
  policies=app-read \
  ttl=1h

What are dynamic secrets and why do they matter?

Static secrets are liabilities. Every database password stored in an environment variable is a potential breach vector that never expires unless manually rotated. Dynamic secrets invert this model: Vault generates unique, short-lived credentials on demand and automatically revokes them when the lease expires. This is the single most valuable feature in secrets management with HashiCorp Vault.

ApplicationVaultDatabaseRequest creds (TTL: 1h)CREATE USER v-token-xyzReturn temp credentialsLease ID + username/passTTL ExpDROP USER v-token-xyz
Dynamic secrets lifecycle: Vault creates temporary database users and automatically revokes them upon lease expiration

Configuring the Database Secrets Engine

The database secrets engine supports PostgreSQL, MySQL, MongoDB, Oracle, and dozens of other datastores. Below is a working PostgreSQL configuration I have deployed across multiple AWS RDS environments:

# Enable and configure the database engine
vault secrets enable database

vault write database/config/postgres-prod \
  plugin_name=postgresql-database-plugin \
  allowed_roles="app-role,readonly-role" \
  connection_url="postgresql://{{username}}:{{password}}@prod-db.cluster-xyz.ap-south-1.rds.amazonaws.com:5432/myapp?sslmode=require" \
  username="vault_admin" \
  password="initial-password-rotate-me"

# Define the role with SQL creation statements
vault write database/roles/app-role \
  db_name=postgres-prod \
  creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
  revocation_statements="DROP ROLE IF EXISTS \"{{name}}\";" \
  default_ttl="1h" \
  max_ttl="24h"

Your application now requests credentials via the Vault API or agent. Each pod or instance receives unique credentials valid for one hour. If a credential leaks, its blast radius is limited to that specific instance and timeframe. When the lease expires, Vault connects to PostgreSQL and drops the user automatically. No cron jobs, no manual rotation scripts, no shared passwords.

How does Vault compare to AWS Secrets Manager and SSM Parameter Store?

A common question from teams already invested in AWS is whether they need Vault at all. The answer depends on your multi-cloud strategy, compliance requirements, and secret complexity. As someone who manages infrastructure across AWS, Azure, and on-premises government data centers, I use both — but for different purposes.

CriteriaHashiCorp VaultAWS Secrets ManagerSSM Parameter Store
Multi-cloud supportNative (AWS, Azure, GCP, on-prem)AWS onlyAWS only
Dynamic secretsFull support (DB, cloud, PKI, SSH)Limited (RDS proxy only)No
Encryption as a serviceTransit engine built-inKMS integration requiredKMS integration required
Identity-based authK8s, LDAP, OIDC, AppRole, cloud IAMIAM onlyIAM only
Cost at scale$0.40/secret/month + API callsFree tier available; Advanced $0.05/param
Compliance auditingDetailed audit device loggingCloudTrail integrationCloudTrail integration
Operational overheadHigh (self-managed HA cluster)Zero (fully managed)Zero (fully managed)

Choose AWS Secrets Manager if you are single-cloud, have simple static secrets, and want zero operational burden. Choose Vault if you need dynamic secrets, operate across multiple clouds or hybrid environments, require encryption-as-a-service, or must meet strict audit requirements where vendor lock-in is a concern. For teams deploying Laravel on AWS as described in our AWS hosting guide, starting with SSM Parameter Store and migrating to Vault when dynamic database credentials become necessary is a pragmatic path.

How do you enforce least-privilege policies effectively?

Vault’s policy language is powerful but unforgiving. A misconfigured policy either blocks legitimate application traffic or grants excessive access. Treat policies as code: version them, review them in pull requests, and test them against staging environments before applying to production.

Policy Design Principles

  • Deny by default. Vault denies all access unless explicitly permitted. Never write permissive wildcard policies like path "secret/*" with write capabilities.
  • Scope by workload identity. Tie policies to Kubernetes service accounts, cloud IAM roles, or AppRole IDs — never to human users for application access.
  • Separate read from write. Applications should almost never have write access to their own secret paths. Only deployment pipelines or administrators should create or update secrets.
  • Use templated policies. Reduce duplication by using identity templating: path "secret/data/{{identity.entity.name}}/*" allows each entity to access only its own namespace.
# Production app policy with scoped access
path "secret/data/apps/myapp/config" {
  capabilities = ["read"]
}

path "database/creds/myapp-role" {
  capabilities = ["read"]
}

# Allow renewal of own leases only
path "sys/leases/renew" {
  capabilities = ["update"]
  allowed_parameters = {
    lease_id = []
    increment = []
  }
}

# Explicitly deny destructive operations
path "secret/metadata/apps/myapp/*" {
  capabilities = ["deny"]
}

Audit every policy change. Enable the file or syslog audit device in production so every access attempt — successful or denied — is logged immutably. During SOC 2 audits, these logs are the primary evidence reviewers examine to verify that access controls function as documented. I have seen audits fail because teams had correct policies but no proof they were enforced consistently.

Static Secrets (Before)Shared DB password in .env fileNo rotation for 18 monthsBreach = full persistent accessVault Dynamic Secrets (After)Unique creds per pod (1h TTL)Auto-rotation on lease expiryBreach = isolated, auto-revokedMigrate
Security posture comparison: static secrets create persistent risk while Vault dynamic secrets contain and automate credential lifecycle

Secure Your Infrastructure Before Scaling

Implementing secrets management with HashiCorp Vault is a foundational step toward production-grade security and audit readiness. Start with a clear threat model, deploy with auto-unseal and Raft storage, adopt dynamic secrets for databases first, and enforce least-privilege policies as code. The operational investment pays dividends in reduced breach surface area and smoother compliance reviews. If your team needs hands-on guidance designing a Vault architecture that fits your existing infrastructure and compliance requirements, reach out to discuss your specific environment.

Frequently Asked Questions

Vault centrally manages secrets, encryption keys, and identity tokens across cloud and on-prem infrastructure. It dynamically generates short-lived credentials for databases, clouds, and PKI while enforcing access policies through code.

Vault is cloud-agnostic and supports dynamic secrets generation natively. AWS Secrets Manager ties you to AWS ecosystems. Vault offers finer-grained policy control and multi-cloud portability without vendor lock-in.

Yes, the open-source version is free under BSL 1.1. Enterprise features like replication, HSM support, and advanced MFA require paid licensing for production environments needing high availability.

Run vault operator init with desired key shares and threshold. Store unseal keys securely offline. Initialize only once per cluster; subsequent restarts require unsealing, not reinitialization.

Integrated Raft storage is recommended for HA clusters. Consul, etcd, and PostgreSQL remain supported but Raft eliminates external dependencies. Filesystem backend suits development only, never production deployments.

Vault creates temporary database users on credential request using configured roles. Credentials auto-expire after TTL. Applications receive unique logins without storing static passwords, reducing breach impact significantly.

Yes, enable the Kubernetes auth method and configure service account bindings. Pods authenticate via projected tokens. Vault validates against the cluster API server and issues mapped secrets automatically.

Distribute key shares across separate secure locations or team members. Use auto-unseal with cloud KMS or transit engine for production. Never store all shares together or in plaintext files.

Sealed servers cannot serve requests until unsealed. Configure HA with integrated Raft or Consul backend. Auto-unseal reduces recovery time. Maintain regular encrypted backups of the storage backend.

Generate a new root token using vault operator generate-root with unseal key verification. Revoke the old token immediately after confirming the new one works. Perform during maintenance windows.

The KV v2 secrets engine stores multiple versions per path. Retrieve specific versions, undelete soft-deleted entries, and configure max versions per mount. Enable versioning at mount creation time.

Enable file or syslog audit devices capturing every request and response. Ship logs to centralized SIEM. Audit logs are append-only; losing them blocks Vault operations by design for safety.

Excessive lease creation, slow storage backends, and insufficient connection pooling cause latency. Tune default TTLs, use batch tokens where possible, and monitor storage IOPS. Scale horizontally before vertically.

The PKI secrets engine issues X.509 certificates dynamically with configurable CAs and roles. Certificates auto-renew before expiry. Integrate with cert-manager in Kubernetes for automated TLS lifecycle management.

Skip Vault for simple single-cloud deployments where native secret managers suffice. Avoid if team lacks operational capacity for HA maintenance. Static config files beat Vault complexity for small projects.