Vault — Reference
Links
- 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) |