Steven's Knowledge

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 version

Start 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 Unsealed

Write 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 version

KV 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/db

Notice 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 app

Tokens (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-id

The 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 | jq

That 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: root

The 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_TOKEN

What'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

On this page