HashiCorp Vault — HA Exercises
Practice high availability concepts with these hands-on exercises.
Exercise 1 — 3-Node Raft Cluster
Task: Set up a 3-node Vault cluster using Raft storage on a single machine (different ports).
Show Answer
# Create data directories
mkdir -p /tmp/vault-{1,2,3}/data
# Node 1 config
cat > /tmp/vault-1/config.hcl <<EOF
storage "raft" {
path = "/tmp/vault-1/data"
node_id = "node1"
}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = true
}
api_addr = "http://127.0.0.1:8200"
cluster_addr = "http://127.0.0.1:8201"
disable_mlock = true
EOF
# Node 2 config
cat > /tmp/vault-2/config.hcl <<EOF
storage "raft" {
path = "/tmp/vault-2/data"
node_id = "node2"
}
listener "tcp" {
address = "127.0.0.1:8210"
tls_disable = true
}
api_addr = "http://127.0.0.1:8210"
cluster_addr = "http://127.0.0.1:8211"
disable_mlock = true
EOF
# Node 3 config
cat > /tmp/vault-3/config.hcl <<EOF
storage "raft" {
path = "/tmp/vault-3/data"
node_id = "node3"
}
listener "tcp" {
address = "127.0.0.1:8220"
tls_disable = true
}
api_addr = "http://127.0.0.1:8220"
cluster_addr = "http://127.0.0.1:8221"
disable_mlock = true
EOF
# Start all 3 nodes
vault server -config=/tmp/vault-1/config.hcl &
vault server -config=/tmp/vault-2/config.hcl &
vault server -config=/tmp/vault-3/config.hcl &
# Initialize node 1
export VAULT_ADDR=http://127.0.0.1:8200
vault operator init -key-shares=1 -key-threshold=1 -format=json > /tmp/init.json
UNSEAL_KEY=$(jq -r '.unseal_keys_b64[0]' /tmp/init.json)
ROOT_TOKEN=$(jq -r '.root_token' /tmp/init.json)
vault operator unseal $UNSEAL_KEY
vault login $ROOT_TOKEN
# Join node 2
VAULT_ADDR=http://127.0.0.1:8210 vault operator raft join http://127.0.0.1:8200
VAULT_ADDR=http://127.0.0.1:8210 vault operator unseal $UNSEAL_KEY
# Join node 3
VAULT_ADDR=http://127.0.0.1:8220 vault operator raft join http://127.0.0.1:8200
VAULT_ADDR=http://127.0.0.1:8220 vault operator unseal $UNSEAL_KEY
# Verify cluster
vault operator raft list-peers
Exercise 2 — Leader Failover
Task: Using the 3-node cluster from Exercise 1, simulate a leader failure.
- Identify the current leader
- Kill the leader process
- Verify a new leader is elected
- Confirm data is still accessible
Show Answer
# Identify leader
vault operator raft list-peers
# Node that shows "leader" state
# Kill the leader (if node1 is leader on port 8200)
kill $(pgrep -f "vault-1/config")
# Wait a moment for election
sleep 5
# Check from another node
VAULT_ADDR=http://127.0.0.1:8210 vault status
# Should show: standby = false (this is the new leader)
VAULT_ADDR=http://127.0.0.1:8210 vault operator raft list-peers
# node1 should be gone or show as failed
# Data is still accessible
VAULT_ADDR=http://127.0.0.1:8210 vault kv get secret/test
Exercise 3 — Raft Snapshots
Task: Create a backup, make changes, then restore the backup.
- Write a secret
- Take a Raft snapshot
- Write another secret
- Restore the snapshot
- Verify the second secret is gone
Show Answer
# Write initial data
vault kv put secret/backup-test value="before-snapshot"
# Take snapshot
vault operator raft snapshot save /tmp/vault-backup.snap
# Write more data
vault kv put secret/after-snapshot value="this-will-be-lost"
# Verify both exist
vault kv get secret/backup-test # ✅ exists
vault kv get secret/after-snapshot # ✅ exists
# Restore snapshot
vault operator raft snapshot restore /tmp/vault-backup.snap
# Check data
vault kv get secret/backup-test # ✅ exists (was in snapshot)
vault kv get secret/after-snapshot # ❌ gone (written after snapshot)
Exercise 4 — Health Checks
Task: Write a script that checks all nodes in a Vault cluster and reports their status.
Show Answer
#!/bin/bash
# vault-health-check.sh
NODES=("http://127.0.0.1:8200" "http://127.0.0.1:8210" "http://127.0.0.1:8220")
echo "=== Vault Cluster Health ==="
for node in "${NODES[@]}"; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${node}/v1/sys/health" 2>/dev/null)
case $STATUS in
200) echo "$node: ACTIVE (leader)" ;;
429) echo "$node: STANDBY (healthy)" ;;
472) echo "$node: DR SECONDARY" ;;
473) echo "$node: PERFORMANCE STANDBY" ;;
501) echo "$node: NOT INITIALIZED" ;;
503) echo "$node: SEALED" ;;
*) echo "$node: UNREACHABLE ($STATUS)" ;;
esac
done
Exercise 5 — Auto-Unseal Configuration
Task: Write a Vault config that uses AWS KMS for auto-unseal, and explain the migration process.
Show Answer
# vault.hcl with AWS KMS auto-unseal
seal "awskms" {
region = "us-east-1"
kms_key_id = "alias/vault-unseal"
}
storage "raft" {
path = "/vault/data"
node_id = "vault-1"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/etc/vault/tls/cert.pem"
tls_key_file = "/etc/vault/tls/key.pem"
}
api_addr = "https://vault-1.internal:8200"
cluster_addr = "https://vault-1.internal:8201"
Migration from Shamir to auto-unseal:
# 1. Stop Vault
systemctl stop vault
# 2. Add the seal stanza to vault.hcl (as above)
# 3. Start with -migrate flag
vault server -config=/etc/vault/vault.hcl -migrate
# 4. Provide Shamir unseal keys when prompted (threshold number)
# Vault re-encrypts the master key with KMS
# 5. Restart normally
systemctl restart vault
# Vault auto-unseals — no keys needed! ✅
What’s Next?
With HA covered, let’s look at Audit & Monitoring — audit logs, telemetry, and alerting.
Next Chapter: Audit & Monitoring