Introduction
HashiCorp Vault is the go-to tool for centralized secret management in 2026. It stores, encrypts, and controls access to credentials like API keys, certificates, or database passwords. Unlike scattered .env files, Vault provides full auditing, automatic rotation, and high availability.
This intermediate tutorial walks you through installing Vault in server mode (not dev), with persistent file storage, a secure TCP listener, HCL policies, the KV v2 engine, and userpass authentication. By the end, you'll manage secrets via CLI and API. Ideal for DevOps or pre-prod environments. Works on Linux/Mac, scalable to Kubernetes. Estimated time: 20 minutes. Why it matters: 70% of breaches come from mismanaged secrets (Verizon DBIR 2025 report).
Prerequisites
- Linux or macOS system (Vault not natively supported on Windows without WSL)
- User with sudo rights for /opt/vault
- 2 GB free RAM
- Basic CLI and encryption knowledge (AES-256)
- Optional: curl for API tests
Download and Install Vault
#!/bin/bash
# Download the latest stable version (1.17.x in 2026)
VAULT_VERSION=1.17.0
wget https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip
# Unzip and install
unzip vault_${VAULT_VERSION}_linux_amd64.zip
sudo mv vault /usr/local/bin/
sudo chmod +x /usr/local/bin/vault
# Verify the installation
vault --version
# Create data directories
sudo mkdir -p /opt/vault/data /opt/vault/logs /opt/vault/config
sudo chown -R $(whoami):$(whoami) /opt/vaultThis script downloads Vault from official HashiCorp releases, installs it in the system PATH, and sets up directories for storage, logs, and config. Run chmod +x install-vault.sh && ./install-vault.sh. Avoid snapshot versions for production; always verify GPG signatures for integrity.
HCL Configuration File
storage "file" {
path = "/opt/vault/data"
}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = false
tls_cert_file = "/opt/vault/config/vault.crt"
tls_key_file = "/opt/vault/config/vault.key"
}
ui = true
log_level = "info"
# Auto-unseal with Transit (optional, manual here for simplicity)
# seal "transit" { ... }
disable_mlock = true
api_addr = "https://127.0.0.1:8200"
cluster_addr = "https://127.0.0.1:8201"This HCL config sets up persistent file storage, a local TCP listener with TLS enabled (generate certs afterward), the web UI, and API/cluster addresses. disable_mlock avoids errors on some OSes. Copy this file; TLS is critical to prevent MITM attacks.
Generate Self-Signed TLS Certificates
#!/bin/bash
cd /opt/vault/config
# Generate private key and self-signed certificate (for local testing)
openssl req -newkey rsa:2048 -nodes -keyout vault.key -x509 -days 365 -out vault.crt -subj "/CN=vault.local"
# Secure permissions
chmod 600 vault.key
chmod 644 vault.crt
# Verify
openssl x509 -in vault.crt -text -noout | grep -A1 "Subject:"Generate basic TLS certs for the listener (use Let's Encrypt in production). Run chmod +x gen-certs.sh && ./gen-certs.sh. This secures API/CLI communications from startup, avoiding 'connection refused' errors without HTTPS.
Start the Vault Server
#!/bin/bash
# Start Vault in foreground (use systemd in production)
nohup vault server -config=/opt/vault/config/vault.hcl > /opt/vault/logs/vault.log 2>&1 &
# Wait for startup
sleep 5
# Check status
curl -k https://127.0.0.1:8200/v1/sys/health | jq .This script launches Vault with the HCL config in the background, redirects logs, and tests health via API (ignores certs with -k for testing). Status '200' means sealed but initialized false. In production, use a systemd service for auto-restart.
Initialize and Unseal Vault
#!/bin/bash
export VAULT_ADDR='https://127.0.0.1:8200'
# Initialize (one time only)
vault operator init > /opt/vault/init-keys.txt
# Retrieve keys (example: replace with your real ones)
UNSEAL_KEY1=$(grep 'Unseal Key 1:' /opt/vault/init-keys.txt | cut -d' ' -f4)
UNSEAL_KEY2=$(grep 'Unseal Key 2:' /opt/vault/init-keys.txt | cut -d' ' -f4)
UNSEAL_KEY3=$(grep 'Unseal Key 3:' /opt/vault/init-keys.txt | cut -d' ' -f4)
# Unseal (3/5 Shamir's)
vault operator unseal $UNSEAL_KEY1
vault operator unseal $UNSEAL_KEY2
vault operator unseal $UNSEAL_KEY3
# Login as root (keep secret!)
ROOT_TOKEN=$(grep 'Initial Root Token:' /opt/vault/init-keys.txt | cut -d' ' -f4)
vault login $ROOT_TOKEN
# Status
vault statusInitialize Vault to generate the root token and unseal keys (Shamir's secret sharing). Replace the extracted keys with yours from init-keys.txt. After 3 unseal operations, Vault is active. Back up init-keys.txt offline; loss means data loss.
Enable KV v2 and Create a Policy
path "secret/data/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "secret/metadata/*" {
capabilities = ["list"]
}This HCL policy grants read/write access to the KV v2 engine at 'secret/'. Run vault policy write kv-rw /tmp/policy-kv.hcl then vault write auth/userpass/users/testuser password=testpass policies=kv-rw to apply it. Separates data/metadata for fine-grained audits.
Store and Retrieve Secrets via CLI and API
#!/bin/bash
export VAULT_ADDR='https://127.0.0.1:8200'
# Enable KV v2
vault secrets enable -path=secret kv-v2
# Create userpass auth
vault auth enable userpass
vault write auth/userpass/users/appuser password=AppPass123! policies=kv-rw
# Login and store secret
vault login -method=userpass username=appuser password=AppPass123!
vault kv put secret/myapp db_password=supersecret123 api_key=sk-abc123
# Retrieve
vault kv get secret/myapp
# Via API curl (with token)
TOKEN=$(vault print token)
curl -s -k -H "X-Vault-Token: $TOKEN" -X GET https://127.0.0.1:8200/v1/secret/data/myapp | jq .data.dataEnable KV v2 for automatic versioning, set up userpass auth, store a secret, and retrieve it via CLI/API. Tokens are ephemeral; use AppRole in production. KV v2 stores metadata separately for rotation without downtime.
Best Practices
- Always enable audit logs:
vault audit enable file file_path=/opt/vault/audit.logfor traceability. - Use auto-unseal with AWS KMS or Transit to avoid manual unseal on boot.
- Least-privilege policies: Granularize by path/capability, avoid root tokens.
- High availability: Switch to Consul/Raft storage for clustering.
- Rotate tokens and use TTL:
vault token create -ttl=1h.
Common Errors to Avoid
- Forgetting to unseal: Vault stays sealed, API returns 5xx; check
vault status. - Misconfigured TLS: 'Certificate signed by unknown authority' errors; use
-kfor tests or trusted CA. - VAULT_ADDR without https: CLI refuses secure connections in 2026.
- Lost init keys: No recovery; mandatory encrypted offline backup.
Next Steps
Master Vault in production with our Learni DevOps training. Resources: Official Vault Docs, Kubernetes Vault Injector Tutorial, advanced HCL community on GitHub.