How Vault Enterprise Actually Counts Clients
An empirical look at entities, tokens, and billing behavior
HashiCorp Vault SME Certified | Passionate about secure secrets management and cloud infrastructure. Sharing insights, tutorials, and best practices on HashiNode to help engineers build resilient, scalable systems. Advocate for DevSecOps and cutting-edge security solutions.
Summary for decision-makers
- Vault Enterprise bills by client identity, not by token count
- Entity-backed authentication (LDAP, OIDC, userpass, AppRole with aliases) produces stable, predictable billing
- Non-entity tokens (direct token creation, orphan tokens, token auth) are billed by unique policy set and can grow unexpectedly
- Migrating to entity-backed auth is the only reliable way to control client counts
Current best practices to control client counts
- Prefer entity-aware auth methods whenever possible
- Use entity-aware auth for CI/CD and bootstrap (AppRole, Kubernetes auth, cloud IAM, JWT/OIDC)
- For lifecycle independence, re-authenticate via entity-aware method instead of using orphan tokens
- Use batch tokens (non-orphan) for short-lived credentials
- Audit regularly with
sys/internal/counters/activityand audit logs - Monitor for policy name changes and refactors (they create new client identities)
If you run Vault Enterprise in production, you will eventually encounter a moment where the client count doesn't match your expectations.
Nothing changed in your workloads. Nothing new was deployed. And yet the number moved—sometimes sharply.
This post exists to explain why.
Not based on assumptions. Not based on sales slides. But based on controlled, repeatable testing against Vault Enterprise.
The short version is this:
Vault Enterprise does not count tokens. It counts clients. And client identity is derived in two fundamentally different ways, depending on how a token was created.
Understanding that distinction explains most billing surprises—and helps you avoid creating them.
Scope and methodology
All findings below are based on:
- Vault Enterprise 1.21.1
- Raft storage
- Tokens were actually exercised against Vault (unused tokens do not count as clients)
- Client counts observed via
sys/internal/counters/activity
Where Vault behavior is documented, I say so. Where behavior is empirical, I am explicit about that.
The two client identity models
Vault Enterprise uses two distinct client identity models.
1. Entity-backed clients (stable, predictable)
When authentication happens via an entity-aware auth method, Vault assigns an entity_id.
Examples:
- userpass
- LDAP
- OIDC / JWT
- AppRole
In this model:
- All tokens associated with the same
entity_idcount as one client - Token policies do not affect client count
- Child tokens inherit the entity automatically
This behavior is documented and stable.
Note: This deduplication can be dramatic. A single entity-backed user with
sudocapability onauth/token/createcan mint tokens with any policy—not just subsets of their own. This bypasses the normal "child policies must be subset of parent" rule (see token create documentation). The result: one user creating hundreds of unique policy set combinations, all collapsing to one client. This is efficient for billing, but means client count no longer reflects actual token or policy diversity.
Effectively, client identity behaves as:
client ~ namespace + entity_id
2. Non-entity clients (policy-based, unstable)
When a token is created without entity context, Vault assigns no entity_id.
In this case, client identity is derived from the policy name combination attached to the token.
Empirically, client identity behaves as if derived from:
client ~ namespace + sorted(unique_policy_names)
This behavior is not fully documented, but it is consistent, repeatable, and observable across versions. And it is the source of most billing surprises.
Important: Since Vault 1.9, non-entity tokens with identical namespace + policy sets are deduplicated into a single client (via a constructed entity). Pre-1.9 behavior was much worse—often counting one client per token. The combinatorial explosion described below still applies, but only unique policy sets count.
The policy-set explosion
For non-entity tokens, each unique set of attached policy names creates a separate billed client. Order is irrelevant—[p1, p2] and [p2, p1] are the same client.
Example:
[p1] -> client A
[p2] -> client B
[p1, p2] -> client C
[p1, p3] -> client D
Four unique policy sets. Four billable clients.
With N policies, the worst case is:
2^N - 1 clients
This is combinatorial growth, not theoretical—and it appears quickly in real systems.
In a real environment with 10 policies, modest variation in policy sets can already produce hundreds of clients where entity-backed authentication would produce one.
How non-entity tokens are created
Non-entity tokens are not edge cases. They are easy to create—often accidentally.
Confirmed creation paths
Direct token creation
vault token create
Common during bootstrap, automation, or CI/CD.
Token chains from a non-entity parent
If the parent token has no entity, children inherit none. The entire chain remains non-entity forever.
Token auth method
auth/token/create
Tokens created via the token auth method have no entity by design.
Orphan tokens
vault token create -orphan
Even when created by an entity-backed user, orphan tokens explicitly drop entity context.
Once a token is non-entity, policy-set-based identity rules apply and client counting becomes unstable.
These paths aren't rare edge cases—they're everyday automation patterns that sneak in during bootstrap, scaling, or CI/CD integration.
Why orphan tokens exist
Orphan tokens solve a real operational problem: lifecycle independence.
They are commonly used for:
- Long-running batch jobs
- CI/CD pipelines
- Service bootstrap tokens
- Cross-team delegation
- Break-glass scenarios
In all cases, the requirement is the same:
"This token must survive the revocation or expiration of the token that created it."
That is exactly what orphan tokens do. But there is a cost.
The critical orphan token rule
Orphan tokens never inherit entity_id. Not sometimes. Not conditionally. Never.
This was tested via:
- Orphan creation from root
- Orphan creation from entity-backed users with sudo
- Orphan token child chains
In all cases:
entity_id = n/a- Token is treated as non-entity
- Policy-based client identity applies
Child vs orphan tokens
| Property | Child token | Orphan token |
| Inherits entity_id | Yes (if parent has one) | No |
| Lifecycle | Revoked with parent | Independent |
| Client identity | Entity-based | Policy-set-based |
| Client explosion risk | Only if parent is non-entity | Always |
There is no native Vault mechanism that provides both:
- lifecycle independence and
- entity-anchored client identity
If you need both, you must re-authenticate via an entity-aware auth method.
Children of orphan tokens
Orphan tokens do not inherit entity context. Neither do their children.
Entity user (entity_id: abc-123)
|
+-- [creates orphan] --> orphan token (entity_id: n/a)
|
+-- child A (entity_id: n/a)
| |
| +-- grandchild (entity_id: n/a)
|
+-- child B (entity_id: n/a)
Once a token becomes non-entity, the entire chain remains non-entity forever. There is no mechanism to recover entity context downstream—the only option is to discard the chain and re-authenticate.
Batch tokens: the safe alternative
Batch tokens behave differently—and this matters.
Batch tokens inherit entity_id like service tokens, but can't be renewed or create children—making them safer for short-lived automation where you don't want token sprawl.
vault token create -type=batch -policy=p1
Result:
- entity_id inherited
- No new client created
However:
vault token create -type=batch -orphan -policy=p1
Result:
- entity_id lost
- New non-entity client (based on policy set)
Batch vs service tokens
| Token Type | entity_id | New Client? | Renewable | Children |
| Service (child) | Inherited | No | Yes | Yes |
| Service (orphan) | Lost | Yes | Yes | Yes |
| Batch (normal) | Inherited | No | No | No |
| Batch (orphan) | Lost | Yes | No | No |
Batch tokens are appropriate when you need:
- Short-lived credentials
- No renewal
- No child tokens
- Entity-anchored billing
From a billing perspective, batch tokens behave like entity-anchored child tokens—unless created with the -orphan flag.
Remediation options
If you're currently using non-entity tokens and want to reduce client count:
| Current State | Recommended Migration | Client Impact |
| Root token -> child tokens | Entity-aware auth method | Reduces to 1 client per identity |
| Orphan tokens for CI/CD | Entity-aware auth with short TTL | Entity-anchored, no explosion |
| Orphan tokens for long jobs | Re-auth via entity-aware method | Fresh entity-backed token |
| Token auth method | Migrate to entity-aware auth | Stable identity |
| Non-entity token chains | Re-auth at chain root | Entire chain becomes entity-backed |
| Short-lived automation | Batch tokens (non-orphan) | Inherits parent entity |
The common theme: re-authenticate via an entity-aware method rather than creating tokens from tokens.
What does not affect client identity
Explicitly tested and not used in client counting:
- Token accessor
- Token creation time
- Parent token
- Periodic vs non-periodic
- Policy content
Policy name matters. Renaming a policy creates a new client. Changing what it allows does not.
Namespaces matter
Client identity is namespace-scoped, which means the same policy set used in different namespaces counts as different clients. This is expected behavior, but it's easy to overlook in multi-namespace deployments where teams might unknowingly duplicate policy structures across namespaces.
Auditing your current exposure
vault read sys/internal/counters/activity/monthly
Warning signs
Look for these patterns in your activity data:
- Non-entity clients significantly outnumber entity clients
- Client count grows after policy refactors or renames
- Many clients with similar or overlapping policy sets
- Client spikes without corresponding workload changes
If you see these patterns, you likely have policy-set-based client identity at work—and it may be worth investigating migration options.
Documentation and stability
HashiCorp explicitly warns that token behavior may change across versions (see the token documentation). That alone is reason to be cautious about relying on undocumented heuristics for billing-sensitive behavior. The patterns described in this post have been stable since Vault 1.9, but always verify after major upgrades.
Why this matters
This behavior enables both:
Over-counting Policy set sprawl leads to combinatorial client growth, which leads to billing surprises.
Under-counting Many workloads anchored under a single entity can collapse into fewer clients than expected—which might seem like a win until you realize your billing doesn't reflect actual usage patterns.
Real-world example: dozens of microservices as 1 client
A client with a lot of microservices each using distinct policy sets but Vault bills them as a single client.
How it works:
- Entity-backed auth (userpass or AppRole) → gets
entity_id sudoonauth/token/create→ bypasses "child must be subset of parent" rule- Creates tokens with any arbitrary policy combination
- All tokens inherit same
entity_id→ 1 client
This is not a bug. It's how entity deduplication works when combined with sudo capability on token creation (see token create documentation).
Trade-off: Billing is efficient, but client count no longer reflects workload diversity. To maintain visibility, consider using audit logs with client ID correlation, or split into multiple entities where usage diversity matters for reporting.
This behavior isn't misuse or misconfiguration. It's simply how Vault works today. The mechanics are consistent and predictable once you understand them, but most teams learn the hard way—staring at an unexpected invoice wondering what changed.
This post exists so you don't have to go through that.
Tested on Vault Enterprise 1.21.1 (Jan 2026). Behavior stable since 1.9, but always verify after upgrades as token mechanics may evolve.
