Skip to main content

Getting Started with HashiCorp Vault: A Practical Guide

Secrets management is one of those problems that seems simple until you’re dealing with hundreds of services, multiple environments, and compliance requirements. That’s where HashiCorp Vault comes in.

After working with Vault at enterprise scale at Dell Technologies, I’ve learned what works (and what doesn’t). This guide covers the fundamentals to get you started.

What is HashiCorp Vault?

Vault is a tool for securely accessing secrets. A secret is anything you want to control access to: API keys, passwords, certificates, encryption keys. Vault provides a unified interface to any secret while providing tight access control and recording a detailed audit log.

Core Concepts

Before diving into code, let’s understand the key concepts:

Secrets Engines

These are components that store, generate, or encrypt data. Common types include:

  • KV (Key-Value): Simple key-value storage for static secrets
  • Database: Dynamic database credentials
  • PKI: Certificate authority for TLS certificates
  • Transit: Encryption as a service

Authentication Methods

How clients prove their identity to Vault:

  • Token: The fundamental auth method
  • AppRole: For machines and applications
  • LDAP/OIDC: For human users
  • Kubernetes: For pods running in K8s

Policies

Define what secrets a client can access:

# Example policy for an application
path "secret/data/myapp/*" {
  capabilities = ["read", "list"]
}

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

Setting Up Vault Locally

Let’s get Vault running for development:

# Install Vault (macOS)
brew install vault

# Start in dev mode (NOT for production!)
vault server -dev

# In another terminal, set the address
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='root'  # Dev mode root token

Your First Secrets

Storing a Secret

# Enable the KV secrets engine (v2)
vault secrets enable -path=secret kv-v2

# Store a secret
vault kv put secret/myapp/config \
  api_key="sk-abc123" \
  db_password="supersecret"

# Read it back
vault kv get secret/myapp/config

Using the API

# Store via API
curl -X POST \
  -H "X-Vault-Token: root" \
  -d '{"data": {"api_key": "sk-abc123"}}' \
  http://127.0.0.1:8200/v1/secret/data/myapp/config

# Read via API
curl -H "X-Vault-Token: root" \
  http://127.0.0.1:8200/v1/secret/data/myapp/config

AppRole Authentication

For applications, AppRole is the recommended auth method:

# Enable AppRole
vault auth enable approle

# Create a policy
vault policy write myapp-policy - <<EOF
path "secret/data/myapp/*" {
  capabilities = ["read"]
}
EOF

# Create a role
vault write auth/approle/role/myapp \
  token_policies="myapp-policy" \
  token_ttl=1h \
  token_max_ttl=4h

# Get the role ID (safe to distribute)
vault read auth/approle/role/myapp/role-id

# Generate a secret ID (keep this secure!)
vault write -f auth/approle/role/myapp/secret-id

Authenticating with AppRole

# Login and get a token
vault write auth/approle/login \
  role_id="<role-id>" \
  secret_id="<secret-id>"

# Use the returned token
export VAULT_TOKEN="<client-token>"
vault kv get secret/myapp/config

Best Practices

From my experience managing Vault at scale:

1. Never Use Root Tokens in Production

Create specific tokens with limited permissions. Root tokens should only be used for initial setup.

2. Enable Audit Logging

vault audit enable file file_path=/var/log/vault_audit.log

Every secret access is logged—essential for compliance.

3. Use Short TTLs

Shorter token lifetimes reduce the blast radius if credentials are compromised.

4. Namespace Your Secrets

Organize by application and environment:

secret/
├── prod/
│   ├── app1/
│   └── app2/
└── dev/
    ├── app1/
    └── app2/

5. Rotate Secrets Regularly

Vault can automate this with dynamic secrets for databases and cloud providers.

Common Pitfalls

Storing secrets in code: Even with Vault, I’ve seen teams commit Vault tokens to Git. Use environment variables or secret injection.

Overly permissive policies: Start restrictive and expand as needed. capabilities = ["create", "read", "update", "delete", "list"] is almost never correct.

Ignoring high availability: Dev mode stores everything in memory. Production needs proper storage backend (Consul, Raft, etc.).

Next Steps

This guide covers the basics. In future posts, I’ll dive into:

  • Dynamic database credentials
  • PKI certificate management
  • Kubernetes integration
  • Vault Agent for secret injection

Resources


Have questions about Vault? Connect with me on LinkedIn—I’m always happy to discuss secrets management strategies.

👨‍💻

Raul C. Peña

Senior Software Engineer at Dell Technologies. Air Force veteran, former real estate broker, self-taught coder. Passionate about DevOps, Hashicorp Vault, and building things that matter.