Vault expresses everything as HTTP paths. Secret engines and auth methods are "mounted," and requests are routed by longest-prefix match. Getting the path design wrong leads to authorization and compatibility pain, so it pays to nail down the fundamentals.
This article walks through how path resolution works, the differences between KV v1 and v2, the login paths used by auth methods, the sys/ administrative API, and the key points for path-based policies — including the pitfalls that show up frequently on the exam.
Vault's HTTP API is rooted under /v1/, and the leading segment splits into roughly three families: sys/ for administration, auth/ for auth methods, and everything else for user-mounted secret engines. The actual routing is resolved by longest-prefix matching the request path against the mount table.
Even on identical-looking paths, the sub-path layout differs by engine. KV v2 uses data/ and metadata/ sub-paths, while Transit uses encrypt/ and decrypt/. The HTTP verb convention is: GET for reads, POST for writes, LIST for listing (or GET with the list=true query parameter).
| Path type | Example | Resolves to | Authorization anchor |
|---|---|---|---|
| Secret engine | /v1/secret/data/app | KV v2 on the secret/ mount | path "secret/data/*" |
| Auth method | /v1/auth/approle/login | login on auth/approle/ | path "auth/approle/login" |
| System administration | /v1/sys/mounts | Mount listing/management API | path "sys/mounts" |
| Identity | /v1/identity/entity | Entity management API | path "identity/*" |
Vault routing resolution (conceptual diagram)
Sanity check: inspect the mount table and routing
export VAULT_ADDR=http://127.0.0.1:8200
export VAULT_TOKEN=<root-or-suitable-token>
# List the mounts
curl -sH "X-Vault-Token: $VAULT_TOKEN" \
$VAULT_ADDR/v1/sys/mounts | jq .
# Listing when LIST is not permitted (the top level of KV v2, for example)
curl -sH "X-Vault-Token: $VAULT_TOKEN" \
"$VAULT_ADDR/v1/secret/metadata?list=true" | jq .Mount names become part of the API path itself, so settle on vocabulary for environments and domains up front, and pick a hierarchy that anticipates future expansion and migration. KV v2 in particular carries data/ and metadata/ sub-paths and is not wire-compatible with v1.
Transit is an encryption service, with a different API and a different responsibility than a storage-backed KV. Trying to retrofit one into the other quickly creates path and permission mismatches, so use separate mounts and separate policies per purpose.
| Engine | Example data path | Versioning | Main auxiliary paths |
|---|---|---|---|
| KV v1 | /v1/kv/app/config | None | n/a |
| KV v2 | /v1/secret/data/app/config | Yes (version selectable) | /v1/secret/metadata/app/config, /v1/secret/destroy/... |
| Transit | /v1/transit/encrypt/orders | N/A | /v1/transit/decrypt/<key>, /v1/transit/rotate/<key> |
KV v2 sub-path layout
secret/ (mount)
|
+-- data/<name> (read and write values: GET/POST)
+-- metadata/<name> (metadata and version management)
+-- destroy/<name> (permanently remove a specific version)
+-- delete/<name> (soft delete: mark a version inactive)
Mount KV v2 at secret/ and read/write a value
# Enable KV v2 (skip if it already exists)
curl -sX POST -H "X-Vault-Token: $VAULT_TOKEN" \
-d '{"type":"kv","options":{"version":"2"}}' \
$VAULT_ADDR/v1/sys/mounts/secret
# Write (KV v2 uses the data/ subpath)
curl -sX POST -H "X-Vault-Token: $VAULT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"data":{"username":"app","password":"s3cr3t"}}' \
$VAULT_ADDR/v1/secret/data/app/config | jq .
# Read (the latest version)
curl -sH "X-Vault-Token: $VAULT_TOKEN" \
$VAULT_ADDR/v1/secret/data/app/config | jq .
# Read a specific version (version=2, for example)
curl -sH "X-Vault-Token: $VAULT_TOKEN" \
"$VAULT_ADDR/v1/secret/data/app/config?version=2" | jq .Auth methods are mounted under auth/<method>/, and the entry point for token issuance is the login endpoint. Applications call login to obtain a Vault token, then make ordinary API calls against secret engine paths.
You can mount the same method twice under different paths and use them side by side (for example, auth/approle/ and auth/approle-ci/). Since changing the path changes the login endpoint, the mount name has to line up in both the client config and the policy.
| Method | Login path | Main request fields | Notes |
|---|---|---|---|
| AppRole | /v1/auth/approle/login | role_id, secret_id | Watch for alias mounts (for example, approle-ci) |
| Kubernetes | /v1/auth/kubernetes/login | jwt, role | Map K8s ServiceAccount JWTs to role names |
| Userpass | /v1/auth/userpass/login/<user> | password | Username in the path, password in the body |
From authentication to reading a secret
App -> POST /v1/auth/<method>/login -> Token
Token -> GET /v1/<mount>/... -> Secret
^ |
|---- Policy ----------|
Example: AppRole login followed by a read
# Log in with AppRole
curl -sX POST -H "X-Vault-Token: $VAULT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"role_id":"<ROLE_ID>","secret_id":"<SECRET_ID>"}' \
$VAULT_ADDR/v1/auth/approle/login | jq .
# Read KV v2 with the client_token you received
APP_TOKEN=<client_token_from_response>
curl -sH "X-Vault-Token: $APP_TOKEN" \
$VAULT_ADDR/v1/secret/data/app/config | jq .sys/ is Vault's administrative API surface, handling enabling and configuring mounts and auth methods, health checks, policies, and token inspection. Application code rarely calls it directly; it shows up heavily in operations and automation (Terraform, CI).
These permissions are powerful, so restrict them to the minimum paths necessary. As a rule, grant sys/mounts/* and sys/auth/* only to administrative roles, never to applications.
| Endpoint | Purpose | Typical verbs | Caveats |
|---|---|---|---|
| /v1/sys/mounts | List/create mounts | GET, POST | Specify type at creation time |
| /v1/sys/mounts/<path>/tune | Tuning (description, version count, etc.) | POST | For example, max_versions on KV v2 |
| /v1/sys/auth | Auth method management | GET, POST | Keep the method type aligned with the path |
| /v1/sys/health | Health check | GET | Used by UIs and load balancers |
| /v1/sys/capabilities-self | Check your own capabilities | POST | Handy for debugging policies |
How sys/ APIs are organized
Typical sys/ operations
# Enable the Transit engine
curl -sX POST -H "X-Vault-Token: $VAULT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"transit"}' \
$VAULT_ADDR/v1/sys/mounts/transit
# Set max_versions on KV v2 to 10
data='{"options":{"version":"2"},"description":"App secrets","max_versions":10}'
curl -sX POST -H "X-Vault-Token: $VAULT_TOKEN" \
-H "Content-Type: application/json" \
-d "$data" \
$VAULT_ADDR/v1/sys/mounts/secret/tune
# Check your own token's rights
curl -sX POST -H "X-Vault-Token: $VAULT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"paths":["secret/data/app/*","sys/mounts"]}' \
$VAULT_ADDR/v1/sys/capabilities-self | jq .Vault ACL policies grant capabilities per path. Because KV v2 treats data/ and metadata/ separately, granting read-only access usually means at least reading data/ and considering list on metadata/. Also distinguish destroy from delete.
Because routing uses longest-prefix match, mixing broad allows with narrow denies makes things complicated fast. As a rule, allow only what is necessary, and avoid wildcards you do not actually need.
| Purpose | Example target path | Required capabilities | Notes |
|---|---|---|---|
| KV v2 read-only | secret/data/app/* | read | Add list on metadata if you need to enumerate |
| KV v2 write | secret/data/app/* | create, update | Initial creation needs create; updates need update |
| KV v2 logical delete | secret/delete/app/* | update | Disables the specified version |
| KV v2 physical delete | secret/destroy/app/* | delete | Irreversible; enforce least privilege strictly |
How a request is evaluated against policy
Request: GET /v1/secret/data/app/config
|
v
[the set of policies]
- path "secret/data/app/*" { capabilities = ["read"] }
- path "secret/metadata/*" { capabilities = ["list"] }
|
v
Verdict: allowed (read matches)Example least-privilege policy and how to verify it
# Policy definition (HCL)
cat > app-readonly.hcl <<'EOF'
path "secret/data/app/*" {
capabilities = ["read"]
}
path "secret/metadata/app" {
capabilities = ["list"]
}
EOF
# Create the policy
curl -sX PUT -H "X-Vault-Token: $VAULT_TOKEN" \
-H "Content-Type: application/json" \
-d @- $VAULT_ADDR/v1/sys/policies/acl/app-readonly <<'JSON'
{"policy": "$(sed 's/"/\\"/g' app-readonly.hcl)"}
JSON
# Self-diagnose your token's capabilities
curl -sX POST -H "X-Vault-Token: $VAULT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"paths":["secret/data/app/config","secret/metadata/app"]}' \
$VAULT_ADDR/v1/sys/capabilities-self | jq .Associate
Question 1
In an environment where KV v2 is mounted at secret/, an application calls GET /v1/secret/app/config and receives a 404. What is the most likely cause?
Correct answer: A
KV v2 reads use /v1/<mount>/data/<name>. /v1/secret/app/config is the KV v1 shape and will return 404 on v2. The correct path is /v1/secret/data/app/config.
Is it safe to rename a mount?
It can affect running applications, so plan it carefully. The common approach is to create a new mount, migrate the data (for example, with the kv migrate tool or dual read/write), and then cut over. During migration it is safest to temporarily grant a policy that allows both paths.
The LIST method is being blocked by my proxy. What should I do?
There is an alternative: pass the list=true query parameter on a GET request. For example, /v1/secret/metadata?list=true. The response format is equivalent to LIST.
I granted only read on KV v2 but my app still cannot list keys.
Listing on KV v2 requires list capability on metadata/. For example, add capabilities=["list"] on path "secret/metadata/app". Reading the actual values still requires read on path "secret/data/app/*".
Practice with certification-focused question sets
Try free questionsNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
Vault Core Concepts: Sealed/Unsealed, Auth, Secrets (2026)
Vault fundamentals — sealed/unsealed state, auth methods, se...
Vault Operations Professional (VOP-003): Complete Guide (2026)
Pass the Vault Operations Professional exam — enterprise pat...
Vault Path-Based Routing: API URL Structure (2026)
How Vault's path-based routing works — mount points, sub-pat...
Vault Tokens: Auth Token Mechanics (2026)
Token fundamentals — service vs. batch tokens, accessor, ren...
Vault Token Types: Service, Batch, Periodic (2026)
Service vs. batch tokens compared — performance, ACL behavio...