Skip to content

Vault — Reference

  • Website: https://www.vaultproject.io
  • HTTP API docs: https://developer.hashicorp.com/vault/api-docs
  • KV v2 API: https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2
  • CLI docs: https://developer.hashicorp.com/vault/docs/commands
  • GitHub: https://github.com/hashicorp/vault

Authentication

All API requests require the X-Vault-Token header. All CLI commands require VAULT_ADDR and VAULT_TOKEN environment variables.

# API
curl -s -H "X-Vault-Token: $VAULT_TOKEN" http://192.168.1.106:8200/v1/...

# CLI (from inside LXC 106)
export VAULT_ADDR=http://127.0.0.1:8200
export VAULT_TOKEN=<token>
vault <command>

The homelab uses KV v2 secrets engine mounted at secret/. All data paths include /data/ in the API URL but not in the CLI path.

API — KV v2 Secrets

Read a secret

curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
  http://192.168.1.106:8200/v1/secret/data/grafana | python3 -c \
  "import json,sys; print(json.dumps(json.load(sys.stdin)['data']['data'], indent=2))"

Write a secret (overwrite all keys)

curl -s -X POST -H "X-Vault-Token: $VAULT_TOKEN" \
  -H "Content-Type: application/json" \
  http://192.168.1.106:8200/v1/secret/data/myservice \
  -d '{"data": {"key1": "value1", "key2": "value2"}}'

Patch a secret (merge keys, keep existing)

curl -s -X PATCH -H "X-Vault-Token: $VAULT_TOKEN" \
  -H "Content-Type: application/merge-patch+json" \
  http://192.168.1.106:8200/v1/secret/data/myservice \
  -d '{"data": {"new_key": "new_value"}}'

Warning: PATCH on KV v2 can be unreliable — see runbook for the recommended read-merge-write pattern via the Forgejo vault-write workflow.

List secrets at a path

curl -s -X LIST -H "X-Vault-Token: $VAULT_TOKEN" \
  http://192.168.1.106:8200/v1/secret/metadata/ | python3 -c \
  "import json,sys; print('\n'.join(json.load(sys.stdin)['data']['keys']))"

Delete a secret

curl -s -X DELETE -H "X-Vault-Token: $VAULT_TOKEN" \
  http://192.168.1.106:8200/v1/secret/data/myservice

API — System Endpoints

Check seal status

curl -s http://192.168.1.106:8200/v1/sys/seal-status | python3 -c \
  "import json,sys; d=json.load(sys.stdin); print(f'sealed={d[\"sealed\"]} initialized={d[\"initialized\"]}')"

No token required for this endpoint.

Token lookup (verify a token)

curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
  http://192.168.1.106:8200/v1/auth/token/lookup-self | python3 -c \
  "import json,sys; d=json.load(sys.stdin)['data']; print(f'policies={d[\"policies\"]} ttl={d[\"ttl\"]}s')"

Renew own token

curl -s -X POST -H "X-Vault-Token: $VAULT_TOKEN" \
  http://192.168.1.106:8200/v1/auth/token/renew-self

Raft snapshot (backup)

curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
  http://192.168.1.106:8200/v1/sys/storage/raft/snapshot -o vault.snap

CLI — Common Commands

These run from inside LXC 106 (ssh [email protected]).

# Status
vault status

# Read
vault kv get secret/grafana
vault kv get -field=admin_password secret/grafana

# Write (overwrite)
vault kv put secret/myservice key1=value1 key2=value2

# Patch (merge)
vault kv patch secret/myservice new_key=new_value

# List
vault kv list secret/

# Delete
vault kv delete secret/myservice

# Token management
vault token lookup
vault token renew

# Unseal
vault operator unseal   # run 3 times with different keys

What the API/CLI Cannot Do

Gap Workaround
No web UI for browsing secrets Use vault kv list / vault kv get CLI, or the API
Unsealing requires 3 separate key submissions Auto-unseal hook handles this on LXC start
PATCH mode can silently lose keys in some edge cases Use the Forgejo vault-write workflow with patch: true (read-merge-write pattern)
No built-in secret rotation Manual rotation + update Forgejo Actions secrets as needed
Vault logs not in Loki Read from file: ssh [email protected] "tail /var/log/vault.log"
Cannot create tokens without a valid token Bootstrap via root token (stored in Vaultwarden)