Vault

Path-Based Routing in Vault: Understanding Mounts and API Structure

2026-04-19
NicheeLab Editorial Team

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.

The Big Picture: Path Routing and Naming Conventions

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).

  • Every API is prefixed with /v1/
  • Mounts are resolved by longest-prefix match (for example, with both payments/ and payments/internal/ present, the latter wins)
  • Reserved paths: sys/, auth/, and identity/ never collide with user mounts
  • Where the LIST method is unavailable, GET with ?list=true works as a substitute
  • A trailing slash is generally accepted either way, but it is safer to be consistent
Path typeExampleResolves toAuthorization anchor
Secret engine/v1/secret/data/appKV v2 on the secret/ mountpath "secret/data/*"
Auth method/v1/auth/approle/loginlogin on auth/approle/path "auth/approle/login"
System administration/v1/sys/mountsMount listing/management APIpath "sys/mounts"
Identity/v1/identity/entityEntity management APIpath "identity/*"

Vault routing resolution (conceptual diagram)

Client/v1/<path>Routersys/*auth/*<mounts>secret/KV v2data/, metadata/payments/KV v1keys...Longest-prefix match: /v1/payments/internal/* prefers payments/internal/

Sanity check: inspect the mount table and routing

export VAULT_ADDR=http://127.0.0.1:8200
export VAULT_TOKEN=<root-or-suitable-token>

# マウント一覧
curl -sH "X-Vault-Token: $VAULT_TOKEN" \
  $VAULT_ADDR/v1/sys/mounts | jq .

# LISTが通らない場合の一覧取得(例: KV v2のトップ階層)
curl -sH "X-Vault-Token: $VAULT_TOKEN" \
  "$VAULT_ADDR/v1/secret/metadata?list=true" | jq .

Designing Secret Engine Mounts (KV v1/v2 and Transit)

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.

  • KV v2 supports versioning. Read/write goes through secret/data/<name>, metadata through secret/metadata/<name>
  • KV v1 is simpler — read/write directly against secret/<name>
  • For Transit, you call encrypt/<key> and decrypt/<key> against a key name
  • If you anticipate migrations, split mounts along business boundaries, environments, or functions (for example, app/ vs. platform/)
  • Include the application name in the first segment to avoid same-name key collisions
EngineExample data pathVersioningMain auxiliary paths
KV v1/v1/kv/app/configNonen/a
KV v2/v1/secret/data/app/configYes (version selectable)/v1/secret/metadata/app/config, /v1/secret/destroy/...
Transit/v1/transit/encrypt/ordersN/A/v1/transit/decrypt/<key>, /v1/transit/rotate/<key>

KV v2 sub-path layout

secret/ (mount)
  |
  +-- data/<name>      (値の読み書き: GET/POST)
  +-- metadata/<name>  (メタ情報・バージョン管理)
  +-- destroy/<name>   (特定versionの完全削除)
  +-- delete/<name>    (論理削除: version無効化)

Mount KV v2 at secret/ and read/write a value

# KV v2を有効化(既にあれば不要)
curl -sX POST -H "X-Vault-Token: $VAULT_TOKEN" \
  -d '{"type":"kv","options":{"version":"2"}}' \
  $VAULT_ADDR/v1/sys/mounts/secret

# 書き込み(KV v2は data/ サブパス)
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 .

# 読み取り(最新版)
curl -sH "X-Vault-Token: $VAULT_TOKEN" \
  $VAULT_ADDR/v1/secret/data/app/config | jq .

# バージョン指定の読み取り(例: version=2)
curl -sH "X-Vault-Token: $VAULT_TOKEN" \
  "$VAULT_ADDR/v1/secret/data/app/config?version=2" | jq .

Auth Method Paths and How login Works

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.

  • AppRole login is auth/approle/login (send role_id and secret_id)
  • Kubernetes login is auth/kubernetes/login (JWT and role)
  • Userpass is auth/userpass/login/<username> (the username lives in the path)
  • OIDC uses additional paths like callback for the browser flow
  • Whenever you change a mount name, update the client config to match
MethodLogin pathMain request fieldsNotes
AppRole/v1/auth/approle/loginrole_id, secret_idWatch for alias mounts (for example, approle-ci)
Kubernetes/v1/auth/kubernetes/loginjwt, roleMap K8s ServiceAccount JWTs to role names
Userpass/v1/auth/userpass/login/<user>passwordUsername 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

# AppRoleでlogin
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 .

# 取得した client_token を使ってKV v2を読む
APP_TOKEN=<client_token_from_response>
curl -sH "X-Vault-Token: $APP_TOKEN" \
  $VAULT_ADDR/v1/secret/data/app/config | jq .

System APIs and Metadata: When to Use sys/

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.

  • sys/mounts: secret engine management (enable, tune, disable)
  • sys/auth: auth method management
  • sys/health: health check (read-only)
  • sys/capabilities[-self]: inspect a token's permissions
  • Lease-related operations live under sys/leases for lookup and revocation
EndpointPurposeTypical verbsCaveats
/v1/sys/mountsList/create mountsGET, POSTSpecify type at creation time
/v1/sys/mounts/<path>/tuneTuning (description, version count, etc.)POSTFor example, max_versions on KV v2
/v1/sys/authAuth method managementGET, POSTKeep the method type aligned with the path
/v1/sys/healthHealth checkGETUsed by UIs and load balancers
/v1/sys/capabilities-selfCheck your own capabilitiesPOSTHandy for debugging policies

How sys/ APIs are organized

Operators / Automation/v1/sys/*Control planemountsauthpolicy ...sys/* is the control plane; applications normally use the /v1/<mount>/* data plane

Typical sys/ operations

# Transitエンジンを有効化
curl -sX POST -H "X-Vault-Token: $VAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"transit"}' \
  $VAULT_ADDR/v1/sys/mounts/transit

# KV v2のmax_versionsを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

# 自トークンの権限を確認
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 .

Path-Based Policies and Practical Permission Design

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.

  • KV v2 read: grant read on path "secret/data/app/*"
  • If you need to list keys, grant list on path "secret/metadata/app"
  • Writes need create and update on data/
  • Permanent version deletion needs delete on destroy/ (grant carefully)
  • As a rule, do not grant sys/ permissions to applications
PurposeExample target pathRequired capabilitiesNotes
KV v2 read-onlysecret/data/app/*readAdd list on metadata if you need to enumerate
KV v2 writesecret/data/app/*create, updateInitial creation needs create; updates need update
KV v2 logical deletesecret/delete/app/*updateDisables the specified version
KV v2 physical deletesecret/destroy/app/*deleteIrreversible; enforce least privilege strictly

How a request is evaluated against policy

Request: GET /v1/secret/data/app/config
   |
   v
[ポリシー集合]
  - path "secret/data/app/*" { capabilities = ["read"] }
  - path "secret/metadata/*" { capabilities = ["list"] }
   |
   v
 判定: 許可(readが一致)

Example least-privilege policy and how to verify it

# ポリシー定義(HCL)
cat > app-readonly.hcl <<'EOF'
path "secret/data/app/*" {
  capabilities = ["read"]
}
path "secret/metadata/app" {
  capabilities = ["list"]
}
EOF

# ポリシー作成
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

# 自トークンの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 .

Check Yourself with a Question

Associate

問題 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?

  1. The request is reading without going through the data/ sub-path required by KV v2
  2. The token has only list capability, not read
  3. sys/mounts has been disabled
  4. An auth/ method has not been enabled

正解: 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.

Frequently Asked Questions

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/*".

Check what you learned with practice questions

Practice with certification-focused question sets

無料で問題を解いてみる
Author

NicheeLab Editorial Team

NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.


Related articles
Vault

Vault Core Concepts: Sealed/Unsealed, Auth, Secrets (2026)

Vault fundamentals — sealed/unsealed state, auth methods, se...

Vault

Vault Operations Professional (VOP-003): Complete Guide (2026)

Pass the Vault Operations Professional exam — enterprise pat...

Vault

Vault Path-Based Routing: API URL Structure (2026)

How Vault's path-based routing works — mount points, sub-pat...

Vault

Vault Tokens: Auth Token Mechanics (2026)

Token fundamentals — service vs. batch tokens, accessor, ren...

Vault

Vault Token Types: Service, Batch, Periodic (2026)

Service vs. batch tokens compared — performance, ACL behavio...

Browse all Vault articles (101)
© 2026 NicheeLab All rights reserved.