Sitemap

A Complete Beginner’s Guide to Secrets Management (Using HashiCorp Vault)

5 min readJul 16, 2026

--

HashiCorp Vault is a security tool used to securely store, manage, and control access to secrets such as passwords, API keys, database credentials, certificates, encryption keys, and tokens. It acts as a centralized “vault” for sensitive information instead of hardcoding secrets into applications or storing them in configuration files.

Why do organizations use Vault?

Imagine you have an application that needs to connect to:

  • A PostgreSQL database.
  • AWS.
  • A third-party API (e.g., Stripe).

Without Vault, you might store credentials in:

  • Environment variables.
  • Configuration files.
  • Source code (which is a bad practice).

With Vault:

  • Applications authenticate to Vault.
  • Vault verifies their identity.
  • Vault provides the required secret securely.
  • Secrets can expire automatically and be rotated without changing application code.

Key Features

  1. Secrets management
    - Store API keys, passwords, SSH keys, certificates, and other sensitive data securely.
  2. Dynamic secrets
    - Generate temporary credentials on demand.
    - Example: Instead of using a permanent database password, Vault creates a database user that’s valid for only one hour.
  3. Encryption as a Service
    - Encrypt and decrypt application data without exposing encryption keys to the application.
  4. Authentication
    - Supports many authentication methods, including LDAP, Active Directory, Kubernetes, AWS IAM, Azure, GitHub, and OIDC.
  5. Access control
    - Uses policies to define exactly who or what can access specific secrets.
  6. Audit logging
    - Records every request to help with security monitoring and compliance.

Vault Workflow

The following image illustrates a complete flow to manage secrets in an applicationF.

Press enter or click to view image in full size
Fig: Vault Workflow

Use Case

Suppose a web application needs a MySQL password.

Without Vault

config.yaml
------------
db_user=admin
db_password=MySecretPassword123

If someone gains access to the file, they have the database password.

With Vault

Press enter or click to view image in full size
Fig: Retrieve password from Vault.

Even if the credentials are stolen, they become unusable after they expire.

Common Integrations

Vault integrates well with:

a) Kubernetes.
b) Docker.
c) AWS, Azure, and Google Cloud.
d) Jenkins and GitHub Actions.
e) Terraform.
f) Databases such as MySQL, PostgreSQL, Oracle, and MongoDB.

Vault Deployment Using Terraform

Kubernetes, Terraform, Helm, and microservices, the Kubernetes + Helm approach would be -

Press enter or click to view image in full size
Fig: Terraform-based Deployment

The overall steps can be described as below -

Step 1: Configure Providers:

terraform {
required_version = ">=1.6"

required_providers {

kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.35"
}

helm = {
source = "hashicorp/helm"
version = "~> 2.15"
}
}
}

provider "kubernetes" {
config_path = "~/.kube/config"
}

provider "helm" {
kubernetes {
config_path = "~/.kube/config"
}
}

Step 2: Create Namespace

resource "kubernetes_namespace" "vault" {

metadata {
name = "vault"
}

}

Step 3: Deploy Vault

resource "helm_release" "vault" {

name = "vault"

namespace = kubernetes_namespace.vault.metadata[0].name

repository = "https://helm.releases.hashicorp.com"

chart = "vault"

version = "0.30.1"

values = [
file("${path.module}/values.yaml")
]
}

Step 4: values.yaml

server:

ha:
enabled: true

raft:
enabled: true

dataStorage:
enabled: true

size: 10Gi

auditStorage:
enabled: true

size: 10Gi

ui:
enabled: true

injector:
enabled: true

This creates:

a) 3 Vault pods.

b) Integrated Raft storage.

c) Persistent Volumes.

d) Web UI.

e) Vault Agent Injector.

Step 5: Initialize Vault

kubectl exec -it vault-0 -n vault -- vault operator init

The output could be -

Unseal Key 1:
Unseal Key 2:
Unseal Key 3:
Unseal Key 4:
Unseal Key 5:

Get Kumaresh Chandra Baruri’s stories in your inbox

Join Medium for free to get updates from this writer.

Initial Root Token: xxxx-YYYY-zzzz

Step 6: Unseal Vault

vault operator unseal

Provide three different keys and repeat on each pod.

Step 7: Login

vault login

Step 8: Enable Kubernetes Authentication

vault auth enable kubernetes

Step 9: Configure Kubernetes

vault write auth/kubernetes/config \
token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
kubernetes_host=https://kubernetes.default.svc

Step 10: Enable KV Secrets

vault secrets enable -path=secret kv-v2

Store a Secret

vault kv put secret/db \
username=admin \
password=Secret123

Read Secret

vault kv get secret/db

Configure the PostGreSQL -

vault write database/config/postgres \
plugin_name=postgresql-database-plugin \
allowed_roles="readonly"

Vault can now generate temporary database users with configurable time-to-live (TTL).

Terraform Deployment

terraform init

terraform plan

terraform apply

Verify

kubectl get pods -n vault

kubectl get pvc -n vault

kubectl get svc -n vault

How Java Service Access?

Add Dependencies

<dependencies>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

</dependencies>

Store Secrets in Vault

Enable the KV secrets engine:

vault secrets enable -path=secret kv-v2

Store a secret:

vault kv put secret/myapp \
db.username=postgres \
db.password=MySecret123

Configure Spring Boot

Application.yml-

spring:

application:
name: myapp

cloud:

vault:

uri: http://localhost:8200

token: hvs.xxxxxxxxxxxxxxxxx

kv:
enabled: true
backend: secret

application-name: myapp

Spring Cloud Vault will automatically read:

secret/myapp

Access Secrets

@RestController
public class SecretController {

@Value("${db.username}")
private String username;

@Value("${db.password}")
private String password;

@GetMapping("/secret")
public String secret() {
return username + " : " + password;
}
}

No database password is stored in application.yml.

Reading Secrets Programmatically

@Service
public class VaultService {

private final VaultTemplate vaultTemplate;

public VaultService(VaultTemplate vaultTemplate) {
this.vaultTemplate = vaultTemplate;
}

public Map<String, Object> getSecrets() {

VaultResponse response =
vaultTemplate.read("secret/data/myapp");

return (Map<String, Object>)
response.getData().get("data");
}
}

Controller:

@RestController
@RequestMapping("/vault")
public class VaultController {

private final VaultService vaultService;

public VaultController(VaultService vaultService) {
this.vaultService = vaultService;
}

@GetMapping("/secrets")
public Map<String, Object> secrets() {
return vaultService.getSecrets();
}
}

Dynamic PostgreSQL Credentials

Enable the database secrets engine:

vault secrets enable database

Configure PostgreSQL:

vault write database/config/postgres \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@postgres:5432/postgres?sslmode=disable" \
allowed_roles="readonly" \
username="postgres" \
password="postgres"

Create a role:

vault write database/roles/readonly \
db_name=postgres \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="30m" \
max_ttl="24h"

Request for temporary credentials-

@Service
public class DatabaseCredentialService {

private final VaultTemplate vaultTemplate;

public DatabaseCredentialService(VaultTemplate vaultTemplate) {
this.vaultTemplate = vaultTemplate;
}

public Map<String, Object> generateCredentials() {

VaultResponse response =
vaultTemplate.read("database/creds/readonly");

return response.getData();
}
}

The response could be -

{
"username": "v-token-abc123",
"password": "P7x9LmK82",
"lease_duration": 1800,
"renewable": true
}

Production Best Practices

a) Use Integrated Raft storage instead of the legacy Consul backend for new deployments.
b) Deploy at least 3 Vault pods across different nodes or availability zones.
c) Enable TLS for all client and cluster communication.
d) Store unseal keys securely (or use Auto Unseal with AWS KMS, Azure Key Vault, or Google Cloud KMS).
e) Enable audit logging.
f) Use Kubernetes authentication instead of static tokens.
g) Apply least-privilege Vault policies.
h) Enable regular Raft snapshots for backup and disaster recovery.
i) Monitor Vault with Prometheus and visualize metrics in Grafana.

References:

a) https://developer.hashicorp.com/vault/docs/about-vault/what-is-vault?utm_source=chatgpt.com

b) https://developer.hashicorp.com/terraform

--

--

Kumaresh Chandra Baruri
Kumaresh Chandra Baruri

Written by Kumaresh Chandra Baruri

Solution Architect specializing in Cloud & Microservices, helping businesses accelerate digital transformation, optimize systems, and deliver scalable solutions