Getting Started
Run Vault in dev mode, write and read a secret, and enable an auth method
Getting Started
This page boots Vault locally in dev mode, walks through the CLI and HTTP API, and gets an app reading a secret with a real auth method (AppRole) rather than a hand-pasted root token.
Dev mode is for learning only. It stores everything in memory, automatically unseals, and prints the root token to your terminal. Production setup is in Best Practices.
Install Vault
# macOS
brew tap hashicorp/tap
brew install hashicorp/tap/vault
# Linux
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vault
vault versionStart Vault in Dev Mode
vault server -dev -dev-root-token-id="root"Output includes:
Root Token: root
Unseal Key: ...
WARNING! dev mode is enabled!In another terminal:
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='root'
vault status # should report Initialized and UnsealedWrite and Read a Secret
The default secret/ mount is a key/value store (KV v2):
# Write
vault kv put secret/app/db username=admin password=hunter2 host=db.example.com
# Read
vault kv get secret/app/db
vault kv get -format=json secret/app/db
vault kv get -field=password secret/app/db # just the value, scriptable
# Update — bumps the version
vault kv put secret/app/db username=admin password=hunter3 host=db.example.com
# History
vault kv metadata get secret/app/db
vault kv get -version=1 secret/app/db # read an old versionKV v2 keeps revisions; you can recover from "oops, overwrote that."
Via the HTTP API
Same operations as REST calls:
# Read
curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
$VAULT_ADDR/v1/secret/data/app/db | jq
# Write
curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
-X POST \
-d '{"data":{"username":"admin","password":"hunter4"}}' \
$VAULT_ADDR/v1/secret/data/app/dbNotice the path: secret/data/app/db (HTTP) maps to secret/app/db (CLI). KV v2 splits the data and metadata namespaces.
Policies — Least Privilege
A policy is HCL that says what tokens can do:
# policies/app.hcl
path "secret/data/app/*" {
capabilities = ["read"]
}
path "secret/metadata/app/*" {
capabilities = ["list", "read"]
}vault policy write app policies/app.hcl
vault policy list
vault policy read appTokens (and auth methods) attach policies. The root token bypasses all policies — never use it from an application.
AppRole Authentication
AppRole gives an app a role_id (like a username) and a secret_id (like a password). The app exchanges them for a short-lived token. No long-lived token sits in the app's environment.
# Enable AppRole
vault auth enable approle
# Create a role bound to the policy we wrote
vault write auth/approle/role/my-app \
token_policies="app" \
token_ttl=1h \
token_max_ttl=4h
# Grab the role_id (public-ish; embedded in deploy config)
vault read auth/approle/role/my-app/role-id
# Generate a secret_id (sensitive; pushed via CI / secret manager / TPM)
vault write -f auth/approle/role/my-app/secret-idThe app then exchanges the pair for a token:
ROLE_ID="..."
SECRET_ID="..."
VAULT_TOKEN=$(curl -s \
--request POST \
--data "{\"role_id\":\"$ROLE_ID\",\"secret_id\":\"$SECRET_ID\"}" \
$VAULT_ADDR/v1/auth/approle/login | jq -r .auth.client_token)
# Use it
curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
$VAULT_ADDR/v1/secret/data/app/db | jqThat token expires in an hour. The app renews it before expiry or re-exchanges role_id + secret_id.
A Real App Pulling a Secret (Node.js)
// node-vault — the most popular Node client
const Vault = require('node-vault');
const vault = Vault({ endpoint: 'http://127.0.0.1:8200' });
// Login with AppRole
const loginResp = await vault.approleLogin({
role_id: process.env.VAULT_ROLE_ID,
secret_id: process.env.VAULT_SECRET_ID,
});
vault.token = loginResp.auth.client_token;
// Read a secret
const secret = await vault.read('secret/data/app/db');
const { username, password, host } = secret.data.data;
// Connect to the DB...Then schedule a renewal loop based on loginResp.auth.lease_duration.
A Quick Tour of the UI
open http://127.0.0.1:8200/ui
# Token: rootThe UI is good for browsing, debugging policies, and one-off operations. In production, automate via CLI / API and CI — the UI is for humans, not workflows.
Tear Down
# Ctrl-C the vault server process; dev mode loses everything on exit
unset VAULT_ADDR VAULT_TOKENWhat's Next
You can store and read secrets with policy + AppRole. The next step is dynamic secrets — the real reason to run Vault:
- Dynamic Secrets — generate short-lived database / cloud / PKI credentials on demand
- Best Practices — HA topology, unsealing, K8s integration, audit, DR