Skip to content

ECMooncakeConnector Usage Guide

ECMooncakeConnector moves multimodal encoder embeddings (encoder cache) between a disaggregated encoder instance and a language-model instance over Mooncake Transfer Engine. Use it when the vision encoder should scale independently of prefill and decode.

For KV-cache transfer between prefiller and decoder, see MooncakeConnector Usage Guide. That path uses MooncakeConnector, not ECMooncakeConnector.

Added in PR #41567. Overview of disaggregated encoding: Disaggregated Encoder.

Important

Static proxy flags (--ec-consumer-zmq-addrs) support Mooncake EC only in 1E + 1PD mode (--prefill-servers-urls disable). For 1E + 1P + 1D, register ec_zmq_addrs on the prefill consumer through the proxy's dynamic registration API instead of the static CLI flag. See examples/disaggregated/disaggregated_encoder/README.md.

Prerequisites

Install Mooncake Transfer Engine:

# CUDA 13 (vLLM default)
uv pip install mooncake-transfer-engine-cuda13

# CUDA 12
uv pip install mooncake-transfer-engine

The two wheels are the same release built against different CUDA majors. Importing the wrong one fails with libcudart.so.<major>: cannot open shared object file.

1E + 1PD

One encoder producer and one combined prefill/decode consumer. The consumer listens on ec_ip + ec_port (ZMQ control). The proxy must advertise that address with --ec-consumer-zmq-addrs.

Encoder (producer)

The producer must run with tensor_parallel_size=1, pipeline_parallel_size=1, and data_parallel_size=1.

CUDA_VISIBLE_DEVICES=0 vllm serve Qwen/Qwen2.5-VL-3B-Instruct \
  --port 19534 \
  --gpu-memory-utilization 0.35 \
  --enable-request-id-headers \
  --no-enable-prefix-caching \
  --max-num-batched-tokens 16384 \
  --allowed-local-media-path /path/to/media \
  --ec-transfer-config '{
    "ec_connector": "ECMooncakeConnector",
    "ec_role": "ec_producer",
    "ec_connector_extra_config": {
      "mooncake_protocol": "rdma"
    }
  }'

Prefill+Decode (consumer)

CUDA_VISIBLE_DEVICES=1 vllm serve Qwen/Qwen2.5-VL-3B-Instruct \
  --port 19537 \
  --gpu-memory-utilization 0.75 \
  --enable-mm-embeds \
  --enable-request-id-headers \
  --no-enable-prefix-caching \
  --allowed-local-media-path /path/to/media \
  --ec-transfer-config '{
    "ec_connector": "ECMooncakeConnector",
    "ec_role": "ec_consumer",
    "ec_ip": "127.0.0.1",
    "ec_port": 19019,
    "ec_connector_extra_config": {
      "mooncake_protocol": "rdma"
    }
  }'

--enable-mm-embeds lets the PD instance inject transferred embeddings instead of re-running the vision encoder.

Proxy

python examples/disaggregated/disaggregated_encoder/disagg_epd_proxy.py \
  --host 0.0.0.0 \
  --port 10002 \
  --encode-servers-urls http://127.0.0.1:19534 \
  --prefill-servers-urls disable \
  --decode-servers-urls http://127.0.0.1:19537 \
  --ec-consumer-zmq-addrs tcp://127.0.0.1:19019

--ec-consumer-zmq-addrs must align with --decode-servers-urls (the combined PD instances). For data-parallel consumers, list each replica consecutively and set --ec-consumer-dp-size.

Correctness script

# TCP (no RDMA device required; CI default)
MOONCAKE_EC_PROTOCOL=tcp \
  bash tests/v1/ec_connector/integration/run_epd_mooncake_ec_full_pipeline.sh

# RDMA
MOONCAKE_EC_PROTOCOL=rdma \
  bash tests/v1/ec_connector/integration/run_epd_mooncake_ec_full_pipeline.sh

The TCP path sets MC_FORCE_TCP=1 so Transfer Engine does not auto-select RDMA when an HCA is present.

Configuration

Producer and consumer must use the same mooncake_protocol.

Setting Where Default Notes
mooncake_protocol ec_connector_extra_config rdma Set tcp when no verbs device is available. With TCP, also export MC_FORCE_TCP=1.
ec_ip / ec_port consumer ECTransferConfig 127.0.0.1 / 14579 First ZMQ control port. Tensor-parallel rank r listens on ec_port + dp_index * tp_size + r.
ec_buffer_size ECTransferConfig 1e9 Registered receive arena on the consumer, in bytes.
control_timeout_s ec_connector_extra_config 30 Reservation RPC timeout.
push_wait_timeout_s ec_connector_extra_config 60 How long the consumer waits for a matching encoder push.
WITH_NVIDIA_PEERMEM process environment 1 Read by Mooncake. Set 0 on hosts without nvidia-peermem (DMA-BUF path).
--ec-consumer-zmq-addrs proxy empty tcp://<ec_ip>:<ec_port> for each PD replica. Required for Mooncake EC.

1E + 1P + 1D

The filesystem example is examples/disaggregated/disaggregated_encoder/disagg_1e1p1d_example.sh (ECExampleConnector for encoder cache, NixlConnector for KV). P→D KV can use MooncakeConnector instead; see MooncakeConnector Usage Guide.

Do not pass --ec-consumer-zmq-addrs together with independent prefill URLs: the static proxy flag is E+PD only. For Mooncake EC with a dedicated prefill, use dynamic registration and set ec_zmq_addrs on the prefill consumer as described in examples/disaggregated/disaggregated_encoder/README.md.

Troubleshooting

Symptom Likely cause
Install mooncake-transfer-engine / libcudart.so.* Wrong or missing Mooncake wheel for this CUDA major.
Failed to register memory ... Bad address [14] GPUDirect registration failed. Set WITH_NVIDIA_PEERMEM=0.
Proxy startup error about E+PD only --ec-consumer-zmq-addrs used with a real --prefill-servers-urls.
Consumer waits then errors; encoder never finishes the push --ec-consumer-zmq-addrs does not match consumer ec_ip/ec_port, or producer/consumer protocols differ.
TCP transfers stall on a host that also has RDMA Export MC_FORCE_TCP=1.
Producer rejected at startup Encoder used tensor, pipeline, or data parallelism. Keep the producer unsharded.