KV offload: a RAM or disk tier behind the KV pool

An offload tier is a second, slower cache for prompt KV. When the GPU’s KV pool is full, vLLM evicts blocks. With a tier the evicted blocks are already in host RAM or on disk, and a request that comes back with the same prefix copies them in instead of running prefill again. It pays when prefixes return: agent sessions resending a long history, preempted requests resuming.

Two tiers in the panel

RAM tierDisk tier
Panel settingkv_offload_ram_gibkv_offload_gib
vLLM flags--kv-offloading-size N --kv-offloading-backend nativeLMCache connector, disk-only config
Wherediscrete-GPU nodes (RTX 5090)the 2x DGX Spark pair
Boundthe box’s /dev/shm (47.1 GiB on the RTX 5090 node; set to 32)NVMe space
Statusin service from 2026-10-12never started with it
SGLang—not offered: SGLang is launched without the connector

RAM tier: how vLLM uses it

vLLM’s own usage gauge for the tier counts only chunks pinned by copies in flight, so it reads 0.0 % on a full tier. The panel derives fill as min(tier size, bytes stored since start) and labels it an upper bound.

Measured: RTX 5090 node, 32 GiB RAM tier

Qwen3.8 27B NVFP4, vLLM 0.31.1rc1, 16 seats, FP8 KV at the time, real Claude Code traffic, 2026-10-11/12. Before vs after the tier, not a controlled same-prompt A/B.

RTX 5090 node, before and after a 32 GiB RAM KV tier: share of prompt tokens computed by prefill 60 percent to 12 percent; decode per stream p50 15 to 49 tokens per second Prompt tokens computed by prefill, % (4 px per %) no tier 60 % 32 GiB RAM tier 12 % Decode per stream, p50, tok/s (6 px per tok/s) no tier 15 32 GiB RAM tier 49
Production traffic, 2026-10-11/12. Lower is better on top, higher below.
Value
Prompt tokens computed by prefill60 % → 12 %
Decode per stream, p5015 → 49 tok/s
Reloaded RAM → GPU467 GiB
Copy speed, reload · store13.4 · 13.1 GB/s
GPU memory free under load~674 MiB
Without the tier (fraction 0.95, no pin)pool full at ~8 parallel 10–20k-token requests, 12 preemptions

Copy speed = bytes ÷ CUDA-event seconds since engine start, read 2026-10-12. Decode rose because less GPU time went to recomputing prefill, not because a pass got cheaper.

For scale, arithmetic: at the 36,166 B/token measured for FP8 KV on this card, 32 GiB holds ~950,000 tokens, about 4x the 235,074-token GPU pool a 7.92 GiB pin gave on FP8.

Cost. The tier takes host RAM (32 GiB of shared memory) and copy bandwidth over PCIe, and GPU free memory under load fell to ~674 MiB. The operator kept it.

Why unified memory refuses a RAM tier

On a DGX Spark the host RAM and the GPU’s memory are one LPDDR5X pool. A RAM tier there would take its bytes from the same memory the KV pool lives in, to cache that pool. The panel refuses it on any unified-memory box.

The disk tier is the option for those boxes: LMCache with local_cpu: false, max_local_cpu_size: 0, a local NVMe directory, 256-token chunks. vLLM’s built-in --kv-offloading-backend lmcache was not used: it wires to a standalone LMCache server whose storage accepts only cpu (LMCache 0.5.4 has the disk branch left as a TODO).

Not proven. On 2026-10-01 the config parsed and the NVMe was writable from inside the container. The engine has never been started with the disk tier, its cost with a zero-size CPU backend is not measured, and the setting defaults to off. Disk speed, hit rate and effect on prefill: not measured.

When it pays

More settings: vLLM settings, explained. Pool size first: KV pool; bytes per token: KV type.

Questions? Let's Chat :)

vllm settings