engine.spec.l2Backend.raw
configuration plus a mounted path inside the engine pod.
Use this when you want:
- a simple on-cluster L2 tier without Redis or object storage
- warm reuse across engine restarts
- a shared POSIX-style backing store, if your cluster can provide one
What this is in the operator model
The chart does not expose a first-classfilesystem.enabled=true value.
Filesystem offloading is configured through the LMCacheEngine CR passthrough:
engine.spec.l2Backend.rawengine.spec.volumesengine.spec.volumeMounts
l2Backend.resp.
Important distinction: shared vs node-local storage
This is the main thing to get right:- if you mount a shared filesystem at the same path on every engine pod, the L2 cache can be shared across nodes
- if you mount a node-local disk or node-local PVC, the L2 cache only helps that node
ReadWriteManynetwork storage is the correct choice when you want cluster-wide reuse- local NVMe, hostPath, or
ReadWriteOncePVCs are still valid, but they behave like node-local offload, not a shared cache
Minimal example
This example mounts a PVC at/data/lmcache/l2 and configures the LMCache fs adapter:
my-values.yaml
What the fields mean
l2Backend.raw.type: fs- selects the filesystem-backed L2 adapter
l2Backend.raw.config.base_path- the directory inside the engine container where LMCache stores L2 files
volumes/volumeMounts- make that path real and writable inside the engine pod
storePolicy: default- keep normal L1 behavior and also store evicted/eligible keys into L2
prefetchPolicy: default- allow misses to load data back from L2 into L1
prefetchMaxInFlight- caps concurrent L2-to-L1 loads to avoid flooding L1 memory
Recommended storage choices
Shared cache across nodes
Use a storage class or backing system that gives you:ReadWriteMany- the same mounted path in every engine pod
- enough throughput for concurrent reads during warm prefills
- NFS
- EFS / Filestore / Azure Files
- CephFS
Fast local spill on each node
Use:- local SSD / NVMe
- hostPath
ReadWriteOncePVC pinned to the node
Optional fs adapter knobs
The fs adapter accepts a few useful extra fields:
relative_tmp_dir- subdirectory under
base_pathfor temporary write files
- subdirectory under
read_ahead_size- issue a small initial read to encourage filesystem readahead
use_odirect- bypass the OS page cache; only use this if you understand the alignment/performance tradeoff
raw.config. The
operator forwards that JSON to LMCache as-is.
Persistence and restart behavior
Filesystem L2 is useful because it survives engine pod restarts as long as the mounted storage survives. That means:emptyDiris not a good choice if you want persistence across pod restarts- a PVC or durable host-backed path is the right choice if restart survival matters
Storage Sizing
Sizing is the next question after “should I use filesystem offloading at all?” The fastest way to pick L1 and L2 capacities is the interactive Cache Sizing guide: paste a workload trace and it simulates LRU reuse distance to show the L1/L2 hit rate you would get at any capacity, measured in characters, tokens, or prefix hashes. To convert those units into bytes of cache, the upstream LMCache KV Cache Size Calculator gives the per-token KV size for a given model.L1 sizing
Use L1 when you want the fastest possible warm-hit behavior. In the operator, L1 is:engine.spec.l1.sizeGB
- size L1 for the hot working set you expect to be reused frequently
- if L1 is too small, warm entries will evict before they are reused
- if L1 comfortably holds the hot set, repeat traffic can stay in memory and avoid L2 reads
l1.sizeGB near the knee of
the hit-rate curve.
L2 sizing
Use L2 when you want capacity beyond RAM or warm reuse across restarts and, if storage is shared, across nodes. General guidance:- size L2 for the larger working set that does not fit in L1
- if L2 is too small, you will churn older entries and lose warm benefit between bursts
- durable L2 is most useful when prompt reuse is real and frequent enough to pay back the extra I/O
Expected performance by hit rate
The performance you should expect depends heavily on where hits land:- mostly L1 hits
- best TTFT improvement
- lowest warm-request latency
- mostly L2 hits
- still useful, but slower than L1 because data must be loaded back into memory
- mostly misses
- little or no cache benefit
Verification
After rollout:- the engine pod is
Running - the mounted path exists and is writable
- the engine does not fail while parsing
--l2-adapter - warm requests eventually produce L2-related store/load activity rather than only L1 behavior
Common mistakes
- mounting a PVC but forgetting to set
l2Backend.raw - setting
base_pathto a directory that is not actually mounted into the pod - assuming node-local storage is shared across nodes
- using
emptyDirand expecting data to survive a pod restart - mounting anything at
/dev/shm- this is unrelated to filesystem L2 and can break CUDA IPC for MP mode
When to use something else
Choose a RESP backend instead when you want:- a clearly shared remote cache tier
- centralized capacity management
- auth-managed network storage with fewer filesystem semantics to think about
- the simplest durable L2 on Kubernetes
- no Redis dependency
- direct use of an existing POSIX storage system

