OnCallReady

Lesson 32.5 · Vault & Secrets Management · 30 min read

Tokens and auth methods: TTLs, renewal, the token tree

In plain words

A token is like a wristband at a festival. You show your ticket once at the gate (that is logging in with an auth method), and from then on you show the wristband everywhere. It has a colour that says which areas you may enter (its policies), and it is only valid for a while (its TTL). You can get it extended at the info desk (renewal), but never past the last day of the festival (the max TTL).

If an organiser hands out wristbands to helpers, those are tied to the organiser's: cancel the organiser's band and all the helpers' bands stop working too (the token tree). A band's serial number lets staff look it up or cancel it without touching it (the accessor).

Why this lesson exists

Ask a platform team for their worst Vault outage and it is rarely Vault being down. It is "the service worked for 32 days and then stopped" - a token reached its maximum lifetime. Or "we revoked one token and three services died" - those services had child tokens of it. Everything you do in Vault, you do with a token, and tokens have lifetimes and family trees. Knowing exactly how they behave is the difference between a five-minute fix and a long night.

What you need to know already: the request shape - client, path, token, policy (the first lesson of this chapter); an unsealed server (the architecture lesson); environment variables and $(...) in bash (6.12).

The words you need first

Logging in is exchanging an identity for a token

           proves who you are                 gets back
person  --- username + password -->  auth/userpass/login/alice  --> token, TTL 30m
CI job  --- role_id + secret_id  -->  auth/approle/login         --> token, TTL 10m
pod     --- ServiceAccount JWT   -->  auth/kubernetes/login      --> token, TTL 1h

Every auth method ends the same way: Vault creates a token with the policies the method's configuration says, and a TTL. From then on Vault does not care how you logged in - it only sees the token. The token auth method is built in and cannot be disabled: it is where all tokens live, whatever method created them.

$ vault auth list
Path      Type     Accessor               Description                Version
----      ----     --------               -----------                -------
token/    token    auth_token_8ae79b34    token based credentials    n/a

Reading a token

Your shell is logged in to the lab Vault with a lab-admin token (the platform team's admin policy - nobody works with the root token day to day):

$ vault token lookup
Key                 Value
---                 -----
accessor            0za7jbcsSimhVT4YcfVLcYfu
creation_time       1790107203
creation_ttl        768h
display_name        token-lab-admin
entity_id           n/a
expire_time         2026-10-24T20:00:03.000757000Z
explicit_max_ttl    0s
id                  hvs.CAESIWOCC8Q48fupiMV6U3IKAALnuugwvYv4yaWQiHGh4KHGh2cy4qoASRy5KPKRPiMrospDo74y6p3p8B8G2
issue_time          2026-09-22T20:00:03.000557000Z
meta                <nil>
num_uses            0
orphan              true
path                auth/token/create-orphan
policies            [default lab-admin]
renewable           true
ttl                 767h59m59s
type                service

Field by field, the ones you will read in incidents:

768h is 32 days: the system default and maximum TTL. Unless something sets a shorter one, every token and every lease lives at most 32 days - which is exactly when the "worked for a month, then stopped" outages happen.

TTL, max TTL and renewal

A token's life:

$ vault token create -policy=default -ttl=10m -explicit-max-ttl=30m -display-name=demo
Key                  Value
---                  -----
token                hvs.CAESITiWmwE2nyBNBF5kPUOriDzdoPqsjJbOfqSuqWGh4KHGh2cy4J8hth5taRXVIwWmjC6jfhde6PZRUfLsA
token_accessor       s32IVHi6tAAaLjeiuCtFiGhV
token_duration       10m
token_renewable      true
token_policies       ["default"]
identity_policies    []
policies             ["default"]

It expires in 10 minutes unless renewed. Renewing asks for more time from now, and Vault caps it at the max:

$ VAULT_TOKEN=$T vault token renew -increment=1h
WARNING! The following warnings were returned from Vault:

  * TTL of "1h" exceeded the effective max_ttl of "29m59s"; TTL value is capped
  accordingly

Key                  Value
---                  -----
token                hvs.CAESI1TO2XdpyZfksSDfZcqA8uKeOrC1sqAc8MUEGPGh4KHGh2cy4Tu2av4MMF4dDErGRwKQxLO8JY4SUEDz1
token_accessor       5elgBuAdDrNnCfehjAOTOp2a
token_duration       29m59s
token_renewable      true
token_policies       ["default"]
identity_policies    []
policies             ["default"]

Read the warning: that is the moment a client can see its token's end coming. Thirty minutes after creation, no renewal helps - the token is revoked, and the only way forward is to log in again. A client that only ever renews, and never re-authenticates, works perfectly until the max TTL and then fails with permission denied on every request.

The max TTL is the smallest of:

  1. the token's own explicit_max_ttl (set at creation);
  2. the auth method's setting for this login (a role's or user's token_max_ttl);
  3. the auth mount's max_lease_ttl (vault auth tune -max-lease-ttl=...);
  4. the system maximum, 768h by default (max_lease_ttl in vault.hcl).

Yes - renewing with a small -increment can shorten a token.

Periodic tokens

A long-running service that cannot re-authenticate easily (no way to get a new secret) can use a periodic token: every renewal sets the TTL back to the period, there is no max TTL, and the token lives forever as long as it renews within the period.

$ vault token lookup $T | grep -E "ttl|period|explicit"
creation_ttl        24h
explicit_max_ttl    0s
period              24h
ttl                 23h59m59s

Convenient, and a liability: a leaked periodic token never expires on its own. Prefer short TTLs and re-authentication through an auth method (AppRole, Kubernetes) - which is exactly what Vault Agent automates, later in this chapter.

The token tree

A token created with vault token create is a child of the token that created it. Revoke the parent and Vault walks down the tree:

$ P=$(vault token create -policy=lab-admin -ttl=1h -field=token)
$ C=$(VAULT_TOKEN=$P vault token create -policy=default -ttl=1h -field=token)
$ O=$(VAULT_TOKEN=$P vault token create -policy=default -ttl=1h -orphan -field=token)
$ vault token revoke $P
Success! Revoked token (if it existed)
$ vault token lookup $C
Error looking up token: Error making API request.

URL: POST https://127.0.0.1:8200/v1/auth/token/lookup
Code: 403. Errors:

* bad token
$ vault token lookup $O | grep -E "orphan|path"
orphan              true
path                auth/token/create-orphan

The child died with its parent; the orphan survived. Everything the child had created - grandchild tokens, database credentials (leases, a later lesson) - is revoked too. That is a feature: revoking one compromised token takes back everything derived from it. It is also how one vault token revoke takes down three services that were given child tokens of a person's token.

Rules worth knowing:

Accessors

You often need to act on a token you do not have (and should not have): the token of a departed colleague, a token found in a log line. Every token has an accessor:

$ vault token lookup -accessor $(vault token lookup -field=accessor) | head -6
Key                 Value
---                 -----
accessor            pQkSk4591PaltrIKWpDz4vqQ
creation_time       1790107203
creation_ttl        768h
display_name        token-lab-admin

vault token revoke -accessor ACCESSOR revokes it. vault list auth/token/accessors (a sudo path) lists every token's accessor - the inventory of who is logged in. Audit logs record accessors (HMAC'd unless configured otherwise), so "revoke whatever that log line used" is possible without anyone ever seeing the token itself.

Use limits and batch tokens

-use-limit=N makes a token die after N requests - good for a one-shot bootstrap:

$ VAULT_TOKEN=$T vault token lookup
Error looking up token: Error making API request.

URL: GET https://127.0.0.1:8200/v1/auth/token/lookup-self
Code: 403. Errors:

* permission denied

Batch tokens (-type=batch, hvb. prefix) are not stored in Vault at all - the token itself carries its data, encrypted. They are cheap at very high volume, but cannot be renewed, cannot create children and do not get revoked when a parent is.

A first auth method: userpass

userpass is a username/password store inside Vault - fine for labs and break-glass accounts; real people log in through LDAP or OIDC (your company SSO), which work the same way from the token's point of view.

$ vault auth enable userpass
Success! Enabled userpass auth method at: userpass/
$ vault write auth/userpass/users/alice password=s3cret-pass token_policies=default token_ttl=30m token_max_ttl=8h
Success! Data written to: auth/userpass/users/alice

The token_* parameters are the same on every auth method's roles and users: which policies the tokens get, their TTL, their max TTL. A working day for a person is a sensible max; a CI job gets minutes.

$ vault login -method=userpass username=alice password=s3cret-pass
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                    Value
---                    -----
token                  hvs.CAESIwKUuW60f93YGKmDIAayEogDZ8ycpkzUibedKxGh4KHGh2cy4QvI7YUUaubUOuTbPifRYFh51xXbifKHt
token_accessor         Xb85vzi0Yhdx682aBhGynh32
token_duration         30m
token_renewable        true
token_policies         ["default"]
identity_policies      []
policies               ["default"]
token_meta_username    alice

Without password= it prompts (Password (will be hidden):), which keeps the password out of shell history - do that outside labs. Vault also creates an entity for alice (entity_id in her token): one identity across auth methods, so the same person logging in with LDAP and OIDC is still one entity with one set of identity policies.

Where the CLI gets its token

  1. VAULT_TOKEN, if set;
  2. otherwise ~/.vault-token (written by vault login - the token helper).

VAULT_TOKEN wins, and vault login warns you:

$ VAULT_TOKEN=hvs.bogus vault login -method=userpass username=alice password=s3cret-pass | head -3
WARNING! The VAULT_TOKEN environment variable is set! The value of this
variable will take precedence; if this is unwanted please unset VAULT_TOKEN or
update its value accordingly.

Two errors tell you which problem you have:

errormeaning
Code: 400. Errors: * missing client tokenno token was sent at all (no VAULT_TOKEN, no ~/.vault-token)
Code: 403. Errors: * permission denieda token was sent, but it is invalid or expired, or it is used up
Code: 403. Errors: * 1 error occurred: * permission deniedthe token is valid, but its policies do not allow this (the next lessons)

Root tokens

A root token has the root policy: every capability on every path, no TTL. Use one only to bootstrap (create the first admin policy and auth method) and in emergencies, then revoke it. When you need root again, a quorum of unseal key holders generates a new one with vault operator generate-root. A root token sitting in a file or a CI variable is the worst secret you can leak.

In an interview: "A service worked for weeks and suddenly gets permission denied from Vault. Where do you look?" - at the token's lifetime: vault token lookup -accessor (from the audit log or the app) for ttl, expire_time and the max TTL; most likely it hit its max TTL (default 768h) because the app only renewed and never re-authenticated. Fix by re-authenticating through its auth method before the max, ideally with Vault Agent.

What you can now do

Why it helps

Most "it worked for weeks and suddenly gets permission denied" tickets are token lifetimes: the app renewed but hit its max TTL (768 hours by default) and never logged in again. Being able to read vault token lookup - ttl, expire_time, policies, orphan, period - and to look a token up by its accessor from the audit log answers those in minutes.

The token tree explains surprises in both directions: revoking an admin token that created app tokens breaks the apps, while revoking a pipeline's token cleanly removes everything it created. And knowing where the CLI finds its token (VAULT_TOKEN, then ~/.vault-token) saves a lot of confusion when two shells disagree.

Commands in this lesson

vault

FAQ

What is the difference between TTL and max TTL?

The TTL is how long the token has left right now; renewing resets it to the increment you ask for (or the default). The max TTL is a hard ceiling counted from when the token was created: no renewal can go past it. When it is reached, the token expires and the client must log in again.

When should I use a periodic token?

For a long-running service that cannot easily re-authenticate. A periodic token has no max TTL: every renewal resets it to the period, so it lives as long as something renews it in time. Keep the period short, so a client that stops renewing loses access soon, and revoke the token when the service is decommissioned.

Why does revoking one token break other things?

Tokens created by a token are its children, and revocation is recursive: the children, their children and every lease they own are revoked too. Use orphan tokens (or tokens from an auth method login, which have no parent) for things that must outlive whoever created them.

What is an accessor and why would I use it?

A second identifier for a token that can look it up, renew it or revoke it, but cannot be used to authenticate. The audit log and vault list auth/token/accessors show accessors, so an operator can find and kill a token without ever seeing or handling the token itself.

The CLI says "missing client token". I just logged in. Why?

The CLI sends VAULT_TOKEN if it is set and otherwise reads ~/.vault-token, which vault login writes. If you logged in as another user (sudo, a different HOME) or another shell has VAULT_TOKEN set to something else, the request goes without the token you expect. vault token lookup shows which one is used.

In an interview Mid

A service worked for weeks and suddenly gets permission denied from Vault. Where do you look?

At the token's lifetime first. I find the token's accessor (from the audit log or the app's logs) and run vault token lookup -accessor <accessor>: if it is gone or the expire_time has passed, it most likely hit its max TTL - 768h by default - because the app only renewed and never re-authenticated. Renewal can extend the TTL only up to the max.

The fix is in the client: log in again through its auth method before the max is reached, ideally with Vault Agent doing it automatically. If the token is still valid, the next suspects are a changed policy or the path (data/ on KV v2).

Also asked: What is the difference between a service token and a batch token? · How do you revoke every token a compromised pipeline created? · Why should the root token not be used for daily work?

Practise this lesson in the terminal Free, in your browser - a real Ubuntu terminal to try it in, with missions that check your work.