HashiCorp Vault Implementation: Centralized Secrets Management
We help companies eliminate tech debt related to storing secrets in .env files and Kubernetes Secrets by implementing HashiCorp Vault. This is not just docker-compose up — it involves designing a fault-tolerant cluster, configuring auto-unseal, integrating with CI/CD, and ensuring full access auditing. Our engineers are HashiCorp certified and have over 5 years of experience with Vault. According to research, 80% of data breaches are linked to compromised credentials, with the average cost exceeding $4 million. Vault reduces this risk by an order of magnitude, with potential savings of up to $3.6 million per prevented incident, making it an indispensable tool.
Vault solves the problem of centralized storage, access auditing, and secret rotation. But the gap between "running Vault in Docker" and a "production-ready Vault cluster" is several days of work. Let's examine the key aspects we consider during implementation.
Why Vault is Better Than Static Passwords
Unlike LastPass or 1Password, Vault doesn't just store passwords — it dynamically generates temporary credentials for databases, cloud services, and APIs. For example, if a secret is compromised, it lives no longer than a specified TTL (usually 1 hour), after which Vault automatically revokes it. This reduces the vulnerability window by 144 times compared to static passwords that may remain valid for weeks.
Architecture: Standalone vs. HA Cluster
For production, a standalone node is unacceptable: single point of failure, no replication, requires manual unseal on restart. The minimum production configuration is a three-node HA cluster with integrated Raft storage.
Vault configuration (vault.hcl) for Raft HA:
ui = true
cluster_addr = "https://vault-1.internal:8201"
api_addr = "https://vault-1.internal:8200"
storage "raft" {
path = "/opt/vault/data"
node_id = "vault-1"
retry_join {
leader_api_addr = "https://vault-2.internal:8200"
}
retry_join {
leader_api_addr = "https://vault-3.internal:8200"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/opt/vault/tls/vault.crt"
tls_key_file = "/opt/vault/tls/vault.key"
tls_client_ca_file = "/opt/vault/tls/ca.crt"
}
seal "awskms" {
region = "eu-central-1"
kms_key_id = "alias/vault-unseal"
}
seal "awskms" — auto-unseal via AWS KMS. Critical for production: without it, each node restart requires manual unseal key entry. With auto-unseal, Vault automatically unseals on startup using the KMS key.
How Services Authenticate to Vault
Vault does not work with a single login/password for all. Each client authenticates via its own method:
Kubernetes Auth — pods authenticate using ServiceAccount JWT tokens. Vault verifies the token through the Kubernetes API.
vault auth enable kubernetes
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc" \
token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
vault write auth/kubernetes/role/myapp \
bound_service_account_names=myapp \
bound_service_account_namespaces=production \
policies=myapp-policy \
ttl=1hAWS IAM Auth — EC2 instances and Lambda functions authenticate via IAM roles. Vault verifies the signature using AWS STS GetCallerIdentity.
AppRole — for CI/CD systems without a built-in authentication method. Role ID is stored in the CI system, Secret ID is generated dynamically and valid once (use_limit=1).
Details of AppRole configuration
vault auth enable approle
vault write auth/approle/role/myapp policies=myapp-policy token_ttl=1h
# Obtain Role ID and Secret ID
vault read auth/approle/role/myapp/role-id
vault write -force auth/approle/role/myapp/secret-id Access Policies: Principle of Least Privilege
Each application receives only the secrets it needs:
# policy: myapp-policy.hcl
path "secret/data/production/myapp/*" {
capabilities = ["read"]
}
path "database/creds/myapp-role" {
capabilities = ["read"]
}
# Deny listing other secrets
path "secret/data/production/+/*" {
capabilities = ["deny"]
}
Apply: vault policy write myapp-policy myapp-policy.hcl
A common mistake: granting a policy with path "secret/*" and capabilities ["read", "list"]. This means a compromised service gains access to the entire secret tree. RBAC via policies is the only way to avoid this.
Vault Agent: Injecting Secrets into Kubernetes Pods
Vault Agent Injector — a sidecar container that mounts secrets as files into the pod without code changes. Pod annotations:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-inject-secret-config.env: "secret/data/production/myapp/config"
vault.hashicorp.com/agent-inject-template-config.env: |
{{- with secret "secret/data/production/myapp/config" -}}
DATABASE_URL={{ .Data.data.db_url }}
API_KEY={{ .Data.data.api_key }}
{{- end }}
Secrets appear in /vault/secrets/config.env. The application reads them as a regular file. On secret rotation, Vault Agent updates the file and can signal the application (SIGHUP) to reload configuration.
Audit Log and Monitoring
Enabling an audit device is mandatory for production:
vault audit enable file file_path=/var/log/vault/audit.log Every request to Vault is logged: who requested, which path, timestamp, result. This forms the foundation for SOC analytics and incident response. Vault will not start if the audit log is unavailable for writing — this is a feature, not a bug. HashiCorp Vault is certified under SOC 2 Type II, confirming reliable secret storage.
Comparison of Secret Management Approaches
| Criterion | .env files | Kubernetes Secrets | HashiCorp Vault |
|---|---|---|---|
| Encryption | None | Base64 (easily decoded) | AES-256 + HSM/KMS |
| Rotation | Manual | Manual | Automatic (dynamic secrets) |
| Audit | None | None | Full log of all requests |
| Role-based access | None | None | RBAC + policies |
| Fault tolerance | None | None | HA cluster with Raft |
Implementation Process
-
Audit of current secret storage — inventory of
.envfiles, Kubernetes Secrets, CI/CD configs -
Design of path hierarchy — structure
secret/data/{env}/{service}/{key}, naming convention - Deploy Vault cluster — Terraform + Ansible, TLS, auto-unseal, initialization
- Configure auth methods for the stack (Kubernetes, AWS IAM, AppRole for CI)
- Migrate secrets — phased, service by service, with verification
- Documentation and handover — runbook for operations: root token rotation, unseal key replacement, disaster recovery
What’s Included in the Work
- Deployed Vault cluster (HA, auto-unseal, TLS)
- Authentication method configuration (Kubernetes, AWS IAM, AppRole)
- Access policies for each service
- CI/CD integration (GitLab CI, GitHub Actions)
- Vault Agent Injector for Kubernetes
- Audit log and monitoring (Prometheus + Grafana)
- Documentation and team training
- 3-month warranty post-implementation
| Scale | Timeline |
|---|---|
| Standalone Vault + single application | 1–2 days |
| HA cluster + multiple services + Kubernetes | 3–5 days |
| Full organization migration (20+ services) | 2–4 weeks |
Cost is calculated individually after infrastructure audit. Our engineers hold the HashiCorp Certified: Vault Operations Professional certification and have executed over 30 secret management projects. Contact us for a consultation — we will assess your infrastructure and offer the optimal solution. Order a turnkey Vault implementation and eliminate tech debt forever.







