Quick Start
Install MemKV, initialize NVMe drives, and start the server.
Deployment shapes
MemKV runs in two shapes. Both use the same wire protocol, authentication, and license.
- Distributed server (Linux). Several servers with NVMe drives and RDMA NICs. This is the shape for production performance, and the one benchmarked at 97.4 GB/s.
- Co-located server. One MemKV server on the same host as the inference engine, such as a
Linux GPU development box, an edge AI node, or an Apple Silicon Mac. It stores data in regular
files and the engine connects over TCP on
127.0.0.1. On Linux, setstorage.mode: file. On macOS this is the only shape, because RDMA, JBOF, and hugepages are not available there.
Requirements for the distributed server
- Linux kernel 6.8 or later.
- An RDMA NIC: Mellanox mlx5 with DC support for the fastest path, or any RoCE NIC.
- NVMe drives used only by MemKV.
memkv setuperases them. - Hugepages for the server's memory pool: at least
memory.max_sizeworth of 2 MiB pages. Reserve them withsudo sysctl -w vm.nr_hugepages=N.memkv startrefuses to start if free hugepage memory is smaller than the pool. - A MemKV Enterprise license.
Requirements for the co-located server
- Linux 6.8 or later, or macOS 13 or later (Apple Silicon or Intel).
- A regular disk for file-mode storage. RDMA, JBOF, and hugepages are not needed.
- TCP transport only. Bandwidth is limited by the host file system and NIC.
For performance, use raw block devices (mode: direct) rather than file mode, in either shape.
The 97.4 GB/s figure comes from the distributed shape on raw block devices over RDMA, so a
co-located server on TCP does not reach it. File mode is for development and for hosts without
raw drives to dedicate.
Run these steps on each MemKV server. One binary contains the server, the admin client, and the documentation site.
Download the binary
curl -LO https://dl.min.io/aistor/memkv/release/linux-amd64/memkv
chmod +x memkv
sudo mv memkv /usr/local/bin/
memkv --versionFor Arm servers, replace linux-amd64 with linux-arm64 in the URL.
Provision a license
Request an Enterprise license at min.io/pricing and save the
license file in /etc/memkv/:
sudo mkdir -p /etc/memkv
sudo install -m 0600 /path/to/minio.license /etc/memkv/minio.licenseThe server finds the license there. To use another path, set MEMKV_LICENSE.
Initialize drives
memkv setup prints the server configuration to stdout. Save it as your config file:
sudo memkv setup \
--drives /dev/nvme0n1 /dev/nvme1n1 /dev/nvme2n1 \
--rdma mlx5_0 \
| sudo tee /etc/memkv/config.yamlThe CLI reference lists every flag.
memkv setup reformats the listed drives and destroys all data on them. To reformat a
drive that MemKV already initialized, add --force.
Start the server
sudo memkv start --config /etc/memkv/config.yamlThe server listens on TCP port 9900 for clients and on HTTP port 9901 for admin requests. Check that it is healthy:
curl http://localhost:9901/v1/health
# {"status":"healthy","rdma_active":true,"drives_online":3,"drives_offline":0,"drives_total":3}Configure the client on each GPU host
The MemKV client plugins read their settings from the YAML file that MEMKV_CONFIG names.
Create one on each GPU host:
sudo mkdir -p /etc/memkv
sudo tee /etc/memkv/client.yaml > /dev/null <<EOF
servers:
- server-0.memkv.example.com:9900
- server-1.memkv.example.com:9900
rdma_devices: [mlx5_0, mlx5_1]
transport: auto # auto picks RDMA when rdma_devices is set, TCP otherwise
auth_key: REPLACE_WITH_SERVERS_AUTH_KEY
license: /etc/memkv/minio.license
EOF
export MEMKV_CONFIG=/etc/memkv/client.yamlSet auth_key to the key that memkv setup generated: copy the network.auth_key value
from the server's /etc/memkv/config.yaml. Copy the license file to the GPU host as well.
Each setting in the file has a matching environment variable that overrides it, such as
MEMKV_SERVERS, MEMKV_RDMA_DEVICES, MEMKV_LICENSE, and MEMKV_AUTH_KEY. See
Configuration → Client for the
full list and the order of precedence.
Next, connect your inference engine: vLLM, sglang, or Dynamo.
Run one MemKV server on the inference host, with file-mode storage and TCP. The same binary works on Linux and macOS.
Write the server configuration
Generate an auth key, save it, and write a configuration that uses one storage file:
sudo mkdir -p /etc/memkv /var/lib/memkv
openssl rand -hex 32 | sudo tee /etc/memkv/auth_key > /dev/null
sudo chmod 600 /etc/memkv/auth_key
AUTH=$(sudo cat /etc/memkv/auth_key)
sudo tee /etc/memkv/config.yaml > /dev/null <<EOF
network:
address: 127.0.0.1:9900 # data plane; admin auto-binds at
# port + 1 (9901)
auth_key: "$AUTH"
memory:
block_size: 2 MiB
max_size: 1 GiB
storage:
mode: file
block_size: 4 MiB
drives:
- media: /var/lib/memkv/drive0.dat
max_size: 16 GiB
EOFThe key is saved in /etc/memkv/auth_key so that the client step can read it from a new
shell.
Start the server
Save the license file to /etc/memkv/minio.license, where the server finds it, and start the
server:
sudo install -m 0600 /path/to/minio.license /etc/memkv/minio.license
sudo memkv start --config /etc/memkv/config.yamlOn the first start, the server allocates the whole storage file, so the first start is slower.
Check that it is healthy. Linux reports drive and RDMA fields; macOS returns the short form:
curl http://127.0.0.1:9901/v1/health
# macOS: {"status":"healthy"}
# Linux: {"status":"healthy","rdma_active":false,"drives_online":1,"drives_offline":0,"drives_total":1}Point the inference client at the server
The MemKV client plugins and the LD_PRELOAD shim read their settings from MEMKV_CONFIG. Set
transport: tcp so the client does not try RDMA. On macOS, tcp is already the default; on
Linux the default is auto, which tries RDMA first.
AUTH=$(sudo cat /etc/memkv/auth_key)
cat > "$HOME/memkv-client.yaml" <<EOF
servers:
- 127.0.0.1:9900
transport: tcp
auth_key: "$AUTH"
license: /etc/memkv/minio.license
EOF
export MEMKV_CONFIG="$HOME/memkv-client.yaml"Any process that loads a MemKV plugin or the LD_PRELOAD shim now sends KV cache traffic to the local server over TCP.
The MemKV Helm chart deploys a StatefulSet with the quay.io/minio/memkv image. It runs one Pod
per server, each on a different node.
Cluster requirements: Kubernetes 1.27 or later (1.34 or later for rdma.mode=dra), RDMA-capable NICs on each storage node,
hugepages reserved on each node, and a license. The chart checks these when you install it.
Create persistent volumes for the NVMe drives
MemKV writes directly to raw block devices. By default (drives.mode: blockDevice), the
chart claims one PersistentVolumeClaim (PVC) per drive per replica. Each claim binds to a
local PersistentVolume (PV) that you create. The PV's nodeAffinity places the Pod on the
host that owns the drive.
Download the example, edit it to match your nodes and drives, then apply it:
curl -fsSLO https://docs.min.io/memkv/charts/local-pv-example.yaml
kubectl apply -f local-pv-example.yamlCreate the license Secret
Request an Enterprise license at min.io/pricing, save it as
minio.license, and load it into the cluster:
kubectl create namespace memkv
kubectl -n memkv create secret generic memkv-license \
--from-file=license=/path/to/minio.licenseChoose how Pods reach the RDMA NICs
Set rdma.mode to one of these values:
rdma.mode | How the Pod gets its NICs | Use it when |
|---|---|---|
hostNetwork (default) | The Pod uses the node's network. Requires securityContext.privileged: true, the default. | You want the simplest setup. |
devicePlugin | The RDMA shared device plugin adds the NICs. Set rdma.devicePlugin.resourceName to the resource it advertises. | The plugin is already deployed in your cluster. |
dra | A Dynamic Resource Allocation (DRA) network driver, such as DraNet, moves the NICs into the Pod. The Pod keeps its normal cluster network. | You run Kubernetes 1.34 or later and want no host networking. |
In devicePlugin mode the Pod also needs an address on the RDMA network, such as a macvlan
attachment through Multus. Add the attachment's k8s.v1.cni.cncf.io/networks annotation
through podAnnotations.
On a server with one NIC per rail, list every NIC in config.rdma.devices, for example
[mlx5_0, mlx5_1]. Give each rail its own IP address on its own subnet. When devices is set,
the chart ignores config.rdma.device.
For dra, create these before you install: a DeviceClass, which is cluster-scoped, and a
ResourceClaimTemplate in the memkv namespace. A Pod gets only the NICs its claim requests,
so request one device for each entry in config.rdma.devices. This template, for DraNet and
a DeviceClass named rdma, requests two NICs:
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: memkv-rdma
namespace: memkv
spec:
spec:
devices:
requests:
- name: rails
exactly:
deviceClassName: rdma
count: 2
selectors:
- cel:
expression: >-
device.attributes["dra.net"].rdma == true &&
device.attributes["dra.net"].state == "up" &&
has(device.attributes["dra.net"].ipv4)DraNet does not offer the NIC that carries the node's default route. To give a Pod two rails, the node's management network must use a different NIC.
Install the chart
Set image.tag to the newest RELEASE tag at
quay.io/minio/memkv.
Recommended. Each drive is a raw block PVC bound to a local PV, and each Pod runs on
the host that owns its drives.
helm install memkv https://docs.min.io/memkv/charts/memkv.tgz \
--namespace memkv \
--set image.tag=RELEASE.2026-11-03T07-55-11Z \
--set replicaCount=2 \
--set drives.blockDevice.count=12 \
--set drives.blockDevice.storageClass=memkv-local \
--set license.existingSecret=memkv-license \
--set config.memory.maxSize="64 GiB" \
--set hugepages.amount=64GiMounts the host's /dev into the Pod, and MemKV uses the /dev/nvmeXn1 paths
directly.
In this mode the chart cannot place Pods on a specific node. Always set nodeSelector
or affinity, so that MemKV does not start on a node without the configured drives.
helm install memkv https://docs.min.io/memkv/charts/memkv.tgz \
--namespace memkv \
--set image.tag=RELEASE.2026-11-03T07-55-11Z \
--set drives.mode=hostDev \
--set 'drives.hostDev.devices={/dev/nvme0n1,/dev/nvme1n1}' \
--set 'nodeSelector.memkv\.minio\.io/storage=true' \
--set license.existingSecret=memkv-licenseVerify the deployment
kubectl -n memkv get pods -o wide
kubectl -n memkv get pvc -l app.kubernetes.io/instance=memkvEach Pod should run on a different node, and every PVC should be Bound. Check the admin
endpoint:
kubectl -n memkv port-forward svc/memkv-admin 9901:9901
curl http://127.0.0.1:9901/v1/health
# {"status":"healthy","rdma_active":true,"drives_online":12,"drives_offline":0,"drives_total":12}