vLLM + MemKV
Run vLLM with MemKV as a durable, shared storage tier behind LMCache.
vLLM moves KV cache through the LMCacheConnectorV1 connector, which hands each chunk to LMCache.
LMCache loads storage backends as plugins, and the MemKV plugin is one of them. vLLM talks to
LMCache, LMCache talks to the plugin, and the plugin talks to a MemKV cluster.
This page connects a vllm serve process to MemKV through LMCache, so KV chunks are stored in
MemKV and read back.
Choose a vLLM path
vLLM can use MemKV in three ways:
| Path | How it works | Use it when |
|---|---|---|
| LMCache (this page) | MemKV is an LMCache storage backend. | You run a vLLM release earlier than 0.25.0, or you already use LMCache. |
| Native tiering | MemKV is a tier behind vLLM's pinned CPU pool. | You want a host memory cache in front of MemKV. |
| Direct offload | MemKV is the offload medium; blocks move between the GPU and MemKV over RDMA. | You want no second copy and no CPU pool to size. |
The two native paths do not use LMCache and need vLLM 0.25.0 or later.
What you need
- A running MemKV cluster. See the Quick Start.
- A MemKV license file (
minio.license). - The MemKV auth key (32 bytes, hex-encoded).
- Network access to PyPI for the
memkv-lmcachepackage. - vLLM and LMCache. The official
vllm/vllm-openaiimage includes vLLM; you install LMCache with pip. - For the RDMA path, one or more RDMA NICs on the GPU host.
Step 1: install the plugin
The memkv-lmcache plugin is on PyPI for x86_64 and aarch64 Linux with glibc 2.28 or later.
Install it with LMCache in the Python environment that vLLM runs in:
pip install 'lmcache>=0.5,<0.6' memkv-lmcacheIn a container, install it when the container starts, as in the docker run example in Step 4.
Step 2: write the LMCache config
Create an LMCache YAML file that selects the MemKV backend:
chunk_size: 256
local_cpu: True
max_local_cpu_size: 60
enable_lazy_memory_allocator: True
lazy_memory_initial_ratio: 0.003
storage_plugins: memkv
extra_config:
storage_plugin.memkv.module_path: memkv_lmcache.backend
storage_plugin.memkv.class_name: MemKVStorageBackendTwo settings are required:
local_cpu: Truewithmax_local_cpu_sizeabove 0. The plugin allocates retrieved tensors from LMCache's CPU pool.storage_plugins: memkv, with theextra_configentries that tell LMCache which module and class to load.
The other settings:
max_local_cpu_sizeis the CPU pool size in GiB. A small pool (single-digit GiB) forces every read through MemKV. A very large pool can compete with CUDA for pinned host memory on systems that use it for NVLink transfers.enable_lazy_memory_allocatorpins the CPU pool in 64 MiB chunks on a background thread, instead of pinning the whole pool at startup. Without it, a pool of several hundred GiB delays startup by tens of seconds, and the first read after a restart pays the same cost. It needs LMCache 0.5 or later, which is why the install command pinslmcache>=0.5,<0.6.lazy_memory_initial_ratiosets the part of the pool pinned at startup. Some drivers limit a single pinned allocation to 2 GiB; on those, keepratio × max_local_cpu_sizeunder 2 GiB (0.003 × 640 GiB ≈ 1.9 GiBfor a 640 GiB pool). Where no such limit applies, you can raise it.chunk_sizeis the number of tokens LMCache writes at a time. Only prompts longer thanchunk_sizesend traffic to MemKV, and each full chunk is one batched store call.
Step 3: configure the MemKV connection
The plugin reads the standard MemKV configuration: the MEMKV_CONFIG YAML file first, then
MEMKV_* environment variables. Environment variables are usually enough:
export MEMKV_SERVERS="host-a:9900,host-b:9900"
export MEMKV_AUTH_KEY="<64-hex-char auth key>"
export MEMKV_TRANSPORT=auto # or rdma; tcp only without RDMA
export MEMKV_LICENSE=/path/to/minio.license
export MEMKV_RDMA_DEVICES="mlx5_0,mlx5_1" # required for the RDMA fast path
export MEMKV_STAGING_SIZE_MB=1024 # per-rank staging pool
export MEMKV_STAGING_SLOT_MB=16 # per-op staging slotThe staging settings set how much pinned host memory each tensor-parallel (TP) worker reserves.
With TP=8, each host reserves 8 × MEMKV_STAGING_SIZE_MB.
Choose the transport
auto(recommended) uses RDMA whenMEMKV_RDMA_DEVICESnames a NIC, and TCP otherwise.rdmauses RDMA only. IfMEMKV_RDMA_DEVICESis not set, it usesmlx5_0on Linux.tcpuses TCP only. Use it only when RDMA cannot reach the servers, for example in a container without/dev/infinibandor on a routed network without RoCE.
In Docker, RDMA needs --device=/dev/infiniband, --cap-add=IPC_LOCK, and
--ulimit memlock=-1, as in the example below.
With tcp, the startup log still shows the RDMA device starting, but all KV transfers use TCP.
To confirm the mode, look for transport: Auto or transport: Rdma in the
Creating memkv engine line.
HMAC authenticates every message but does not encrypt it, so use TCP only on a network you control. See Encryption.
Step 4: launch vLLM with the LMCache connector
Pass LMCacheConnectorV1 in --kv-transfer-config, and point LMCache at the YAML file from
Step 2 with LMCACHE_CONFIG_FILE:
docker run -d --name vllm-memkv \
--runtime=nvidia --net=host --shm-size=64g --ipc=host \
--device=/dev/infiniband --cap-add=IPC_LOCK --ulimit memlock=-1 \
-e CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
-e MEMKV_SERVERS="host-a:9900,host-b:9900" \
-e MEMKV_AUTH_KEY="$AUTH_KEY" \
-e MEMKV_TRANSPORT=auto \
-e MEMKV_RDMA_DEVICES="mlx5_0,mlx5_1" \
-e MEMKV_LICENSE=/minio.license \
-e LMCACHE_CONFIG_FILE=/lmcache.yaml \
-v /path/to/models:/inference-models:ro \
-v /path/to/minio.license:/minio.license:ro \
-v /path/to/lmcache.yaml:/lmcache.yaml:ro \
--entrypoint bash \
vllm/vllm-openai:<tag> -lc '
pip install "lmcache>=0.5,<0.6" memkv-lmcache &&
vllm serve /inference-models/<model-dir> \
--host 0.0.0.0 --port 8810 \
--tensor-parallel-size <N> \
--trust-remote-code \
--max-model-len <max_seq_len> \
--gpu-memory-utilization 0.85 \
--enable-prefix-caching \
--kv-transfer-config "{\"kv_connector\":\"LMCacheConnectorV1\",\"kv_role\":\"kv_both\"}"
'Check the startup log
A clean start logs these lines for each TP worker, in this order:
Successfully installed memkv-lmcache-<version>: pip installed the plugin.LMCache INFO: Creating LMCacheEngine with config: {... storage_plugins: ['memkv'] ...}: LMCache read your YAML file.LMCache INFO: Created dynamic backend: memkv: LMCache loaded the plugin.INFO Creating memkv engine config=PluginConfig {...}: the plugin started its client.INFO memkv-client license verified plan=...: the license is valid.INFO Server mapped to rail server="...": the client registered a MemKV server. There is one line for each server.INFO MemKVStorageBackend ready (servers=[...], rdma=..., dst_device=...): the plugin is ready.
When the first prompt longer than chunk_size arrives, LMCache logs lines like:
LMCache INFO: [req_id=...] Stored ... tokens. ...
... offload_time: ... put_time: ...put_time is the time the plugin spent storing the chunk in MemKV.
What this integration gives you
- Capacity beyond host memory. When LMCache's CPU pool is full, evicted chunks go to MemKV instead of being dropped. The prefix cache becomes (host RAM × replicas) plus the MemKV cluster.
- Sharing across replicas. vLLM processes that point at the same MemKV cluster share one chunk pool. A system prompt used by many replicas is stored once.
- Durability. Stored chunks survive vLLM and LMCache restarts, and a restarted process reads them back.
- Authentication. Every operation is authenticated with HMAC and the cluster's shared key.
Operational notes
- Connections per rank. LMCache runs one cache engine per TP worker, and each has its own MemKV client. With TP=N, each MemKV server sees N sets of connections.
- The license is required. The plugin checks the license when it starts. Without a valid
license, LMCache cannot start the backend and vLLM stops. Mount the license file and set
MEMKV_LICENSE, or set it in theMEMKV_CONFIGfile.