Quick Start
Disaggregation is controlled by a single flag:--disagg-role. Each component is launched independently, just like LLM PD disaggregation.
—disagg-role | What it runs |
|---|---|
monolithic | (Default) Standard single-server mode |
encoder | All stages with the default RoleType.ENCODER affinity: InputValidationStage, TextEncodingStage (plus ImageEncodingStage / ImageVAEEncodingStage for image-conditioned pipelines), LatentPreparationStage, TimestepPreparationStage, and any model-specific “before denoising” stage (e.g. QwenImageLayeredBeforeDenoisingStage, GlmImageBeforeDenoisingStage). |
prompt_encoder / image_encoder | Separate text and reference-image encoding stages (currently FLUX.3 Image). |
denoiser | DenoisingStage (and its subclasses: CausalDMDDenoisingStage, DmdDenoisingStage, LTX2AVDenoisingStage, LTX2RefinementStage, Hunyuan3DShapeDenoisingStage, …) — the DiT forward loop plus the scheduler stepping it drives. |
decoder | DecodingStage (VAE decode) and its subclasses (LTX2AVDecodingStage, HeliosDecodingStage, …). |
server | DiffusionServer head node + HTTP server (no GPU) |
Each stage declares its role via therole_affinityproperty onPipelineStage(defaultENCODER). Non-monolithic pipelines only instantiate matching stages. The combinedencoderrole also includes stages withPROMPT_ENCODERorIMAGE_ENCODERaffinity.
Single-Machine Example (Verified)
The following commands have been tested end-to-end on an 8×H200 machine withWan-AI/Wan2.1-T2V-1.3B-Diffusers. Each role runs on a separate GPU via
--base-gpu-id; the server head node requires no GPU.
Tested result (8×H200): Encoder 2.3 s (TextEncoding) → Denoiser 312.8 s (50 steps, layerwise offload) → Decoder 7.1 s (VAE decode). Total ~322 s for 81-frame 1024×1024 video.
Tip:--base-gpu-idcontrols which physical GPU the role uses. Encoder and Decoder can share a GPU (e.g. both--base-gpu-id 0) to save resources, but make sure the combined GPU memory is sufficient.
Multi-Machine Example
The exact same CLI pattern — just replace127.0.0.1 with actual IPs and add
RDMA flags for direct transfer:
ZMQ handles startup order gracefully — instances and head can start in any order.
FLUX.3 split encoding
FLUX.3 Image supports both the three-role topology above and four independently deployable worker pools:hf auth login, or provide an accessible local package path.
Install the transfer dependencies described below on every worker.
For long prompts, increase --disagg-transfer-pool-size on the encoding and
denoising workers to cover their largest incoming/outgoing tensors, as well as
reserved receive slots; for example, 1073741824 reserves a 1 GiB pool.
For a Linux NVIDIA deployment with four GPUs, start the workers in one shell:
/v1/images/generations for T2I and
multipart /v1/images/edits for references:
reference.png with your input image. Omit size on edits to keep the
reference dimensions, including the model’s padding/crop behavior. The head
inlines locally uploaded reference files before dispatch, so GPU workers do
not need access to the head’s upload directory. URL inputs must remain
reachable from the image encoder.
Specify both --prompt-encoder-urls and --image-encoder-urls, instead of
--encoder-urls. Each accepts semicolon-separated worker endpoints, with the
same dispatch policy as the existing pools. Alternatively, use the existing
encoder role and --encoder-urls to colocate both encoders in one worker.
Image encoder and decoder processes can also share a GPU if their combined
weights, transfer pools, and concurrent activations fit.
Across machines, replace loopback addresses with routable IPs. Advertise each
worker’s IP with --disagg-p2p-hostname, and use that exact work endpoint in
the head’s URL list. The head also listens on scheduler port +4 for prompt
encoder results and +5 for image encoder results; +1/+2/+3 retain their
existing meanings. Keep worker work/control ports (+1), internal scheduler
ports, and distributed master ports distinct. Run the internal transport on a
trusted private network, not a public interface.
The BF16 flow-map package has been validated on H200 with same-node,
multi-process Mooncake RDMA: concurrent T2I, reference-size-preserving i2i,
and both three-role and four-role topologies matched the monolithic API’s
output pixels with quality=exact. Cross-node execution has not been validated.
Multiple Instances per Role
Use semicolons in--*-urls to register multiple instances:
GLM-Image distributed mode
GLM-Image can batch AR generation in the head and dispatch the resulting prior tokens to distributed denoiser workers. Each worker runs prompt/glyph preparation, DiT, and VAE decoding locally; no latent or embedding tensors are transferred. The following 16-device deployment uses devices 0-1 for the external AR server and devices 2-15 for 14 independent batch-1 Cache-DiT denoisers.--encoder-urls and --decoder-urls are intentionally
omitted for this topology.
--output-path. The PR benchmark used
longtext-bench.zip:
Port Convention
Result endpoints are derived deterministically from the head node’s--scheduler-port (default: 5555):
| Socket | Port |
|---|---|
| DS frontend (ROUTER) | scheduler_port |
| Encoder result (PULL) | scheduler_port + 1 |
| Denoiser result (PULL) | scheduler_port + 2 |
| Decoder result (PULL) | scheduler_port + 3 |
| Prompt encoder result (PULL, split encoding) | scheduler_port + 4 |
| Image encoder result (PULL, split encoding) | scheduler_port + 5 |
--disagg-server-addr. No manual endpoint configuration needed.
Transfer Mechanism
Tensor data between adjacent roles is transferred via a P2P transfer engine. The DiffusionServer routes control messages and request metadata; tensors flow directly between workers. Uploaded reference files are forwarded inline as request metadata. mooncake-transfer-engine is required for disaggregated diffusion. It provides RDMA for direct GPU-to-GPU data movement. The GLM-Image distributed mode is an exception: it relays only prior token IDs and request metadata over ZMQ and does not require Mooncake.Transfer Flow
- Sender stages output tensors in its transfer buffer, then notifies DiffusionServer.
- DiffusionServer selects a downstream worker and reserves one of its preallocated receive slots. If none fits, it sends
transfer_allocand waits fortransfer_allocated. - DiffusionServer sends
transfer_pushto the sender with the receiver’s session and destination address. - Sender pushes the tensors through Mooncake and reports
transfer_pushedafter completion. - DiffusionServer sends
transfer_readyto the receiver and releases the sender’s scheduling slot. - Receiver loads the tensors on its transfer stream and executes its stages. Receive prefetch can overlap the previous request’s compute.
Failure Isolation
The head rejects malformed scheduler requests and duplicate active request IDs without replacing the original request. Each scheduler message carries one request; use concurrent API calls for independent requests, orn for multiple
outputs from one request.
Tensor decoding errors fail only the associated request when its ID can be
recovered. Results without an identifiable request are logged and discarded;
affected requests still rely on --disagg-timeout. Reported worker errors release
their scheduling capacity and reserved receive slots so later requests can run.
Keep scheduler and worker ports on a trusted private network. This error handling
does not make the pickle-based scheduler protocol safe for untrusted clients, or
provide durable jobs and transparent recovery after a head restart.
RDMA Flags
| Flag | Default | Description |
|---|---|---|
—disagg-p2p-hostname | 127.0.0.1 | RDMA-reachable hostname/IP of this instance |
—disagg-ib-device | None | InfiniBand device (e.g., mlx5_0, mlx5_roce0) |
—disagg-transfer-pool-size | 256 MiB | Pinned memory pool per instance |
--disagg-p2p-hostname to the actual IP on each machine. For multi-machine, --disagg-ib-device specifies the RDMA NIC.
Per-Role Parallelism
| Flag | Description |
|---|---|
—encoder-tp | Encoder tensor parallelism |
—denoiser-tp / —denoiser-sp / —denoiser-ulysses / —denoiser-ring | Denoiser parallelism |
—decoder-sp | Decoder sequence parallelism |
--tp-size and --sp-degree. Otherwise, parallelism is auto-derived for each
role’s --num-gpus (or its GPU list in the Python pool API). Automatic CFG,
sequence-parallel and HSDP choices from the parent configuration are not reused
for a role with a different device count. Explicit incompatible settings still
raise an error; they are not silently reduced to fit.
Other Options
| Flag | Default | Description |
|---|---|---|
—disagg-timeout | 600 | Timeout (seconds) for pending requests |
—disagg-dispatch-policy | round_robin | round_robin or max_free_slots |
Python API
For programmatic single-machine deployment,launch_pool_disagg_server() is available. It spawns its workers, and each child re-executes this script’s module scope before initializing its platform, so keep the diffusion imports inside the guard:
Architecture
Request State Machine
FAILED or TIMED_OUT.