Skip to main content
When several tenants, users, or environments share one LMCache deployment, you usually want two kinds of isolation:
  • KVCache isolation — control who is allowed to share cached prefixes. A prompt cached for one tenant must not be reused by another. This is what cache_salt provides.
  • Capacity isolation — control how much cache each tenant may consume, so one noisy tenant cannot evict everyone else’s entries. This is what per-tenant quotas and the IsolatedLRU eviction policy provide.
This page covers both, and which layer owns each setting.

Who configures what

Multi-tenancy spans three layers. Putting a setting in the wrong layer is the most common mistake.

KVCache isolation with cache_salt

cache_salt is a per-request isolation key. It tells TMO which requests are allowed to share cached KV and which ones must stay separate. The behavior is simple:
  • same model + same cache_salt → kv cache access is allowed
  • same model + different cache_salt → kv cache access is blocked

How to set it

cache_salt is a first-class field on OpenAI-compatible requests (vLLM). Include it directly in the request body.

How to choose a value

Pick a value that is:
  • stable across requests that should share cache
  • different across requests that must stay isolated
  • low-cardinality enough that it does not explode your metrics labels
Good choices: tenant-acme, customer-123, prod, workspace-42. Bad choices: a random UUID per request, a timestamp, or any value that changes on every call. If the value changes on every request, you destroy reuse and effectively turn every request into a cache miss.

Constraints

cache_salt is validated by TMO. It:
  • must not contain @
  • must not contain /
  • must not contain \
  • must not contain a NUL byte
  • must be at most 128 characters
A salt that violates these is rejected with a ValueError. Keep it simple: short ASCII identifiers such as tenant-acme or prod are the safest choice.
The empty string ("") is the default, unsalted domain. In the cache-admin HTTP API described below, the empty salt cannot appear in a URL path, so it is represented by the reserved sentinel _default (for example GET /quota/_default).

Capacity isolation: per-tenant quotas

KVCache isolation keeps tenants from reading each other’s data. It does not stop one tenant from filling the cache capacity. For that, TMO supports per-tenant quotas backed by the IsolatedLRU eviction policy. How it works:
  • IsolatedLRU keeps a separate LRU ordering per cache_salt instead of one global list, so eviction victims for one tenant are chosen only from that tenant’s entries.
  • A quota assigns each cache_salt a storage budget in bytes. LMCache tracks usage per salt and evicts a salt’s oldest entries once it crosses its budget.
  • Quotas use allowlist semantics: a cache_salt with no registered quota has an effective limit of 0 bytes. Its stores are accepted on the hot path but evicted on the next eviction cycle (~1s). Only salts with an explicit quota retain cached data. Plan to register a quota for every tenant you want cached.
The eviction loop is governed by these engine-side fields (LMCache eviction config):

Managing quotas at runtime

The engine exposes an HTTP cache-admin frontend on spec.httpPort (default 8080). Quotas are created, inspected, and removed there:
Use the _default sentinel to manage the unsalted domain: PUT /quota/_default.

Fleet-wide quotas via the coordinator

The HTTP API above is per-engine. To track and bound a tenant’s usage across an entire fleet of engines, register the engines with an LMCacheCoordinator and enable event reporting on the engine’s coordinator block:
With reporting on, each engine forwards per-salt usage events to the coordinator, which aggregates them and runs fleet-wide per-salt quota enforcement and eviction. The coordinator side exposes the matching admin endpoints (PUT/DELETE /l2/quota/{cache_salt}, GET /l2/status[/{cache_salt}]) and its own sweep controls (evictionCheckInterval, default 5s; evictionRatio, default 0.2).

Deployment-level isolation

cache_salt and quotas isolate tenants within one shared cache. For harder isolation, give each tenant its own engine:
  • Per-namespace engines — run a separate LMCacheEngine CR per tenant namespace. The chart’s multi-tenant mode (operator.enabled=true, engine.enabled=false) installs only the operator; each tenant then ships their own engine CR. Caches are not shared across separate engines at all.
  • Node placementspec.nodeSelector, spec.affinity, and spec.tolerations pin a tenant’s engine to dedicated GPU nodes.
  • RBAC / networkspec.serviceAccountName, spec.podLabels, and spec.podAnnotations let you attach per-tenant RBAC and NetworkPolicy.
Choose deployment-level isolation when tenants must not share GPUs or memory at all; choose cache_salt + quotas when you want the efficiency of one shared pool with logical separation.

Observability

LMCache tags several metrics with cache_salt, which is useful for per-tenant hit-rate analysis:
  • lmcache_mp.lookup_requested — tokens submitted for lookup (labels: model_name, cache_salt)
  • lmcache_mp.lookup_hit — tokens served from cache (labels: model_name, cache_salt)
  • lmcache_mp.real_reuse_gap — seconds between a chunk’s last write and next read (label: cache_salt)
  • lmcache_mp.real_reuse_gap_objects — the same gap measured in chunks (label: cache_salt)
Divide lookup_hit by lookup_requested, grouped by cache_salt, for a per-tenant hit rate.
Treat cache_salt as a potentially high-cardinality dimension. Per-tenant or per-environment salts are fine; per-request salts will blow up your metrics cardinality (and destroy reuse).

Verification

cache_salt reuse behavior is straightforward to check by hand:
  1. Send a long prompt with cache_salt: "tenant-a".
  2. Send the same prompt again with cache_salt: "tenant-a" → reuse is allowed.
  3. Send the same prompt with cache_salt: "tenant-b" → it must not reuse tenant-a’s entries, even though the prompt is identical.
To confirm a hit at the engine level, inspect the engine pod logs — the engine logs KV cache registration and serving activity that the end-to-end test harness keys on to assert cache behavior. Per-tenant hit rates are also visible through the metrics above.
The current end-to-end test suite exercises basic cache hit/miss and L1/L2 storage, but does not yet include automated coverage for cache_salt isolation, per-tenant quotas, or IsolatedLRU. Validate these flows in a staging deployment before relying on them in production.
  • Use no cache_salt if all traffic is inside one trust boundary.
  • Use one stable salt per tenant or environment when traffic must be isolated.
  • Avoid per-request salts unless you explicitly want to disable sharing.
  • If you need capacity fairness, enable IsolatedLRU and register a quota for every tenant you want cached (remember: unregistered salts get 0 bytes).
  • For strict isolation, give each tenant its own engine per namespace instead of a shared pool.