- Independent scaling. Add or remove rollout capacity without resizing or restarting the trainer.
- Flexible placement. Run inference where GPUs are available, including other clusters, regions, and providers.
- Separate failure domains. Replace rollout replicas without making their process lifecycle part of the training job.
- Better asynchronous utilization. Keep a changing pool of rollout workers busy while training consumes completed trajectories.
- Policy correctness. Publish immutable policy versions, constrain requests by version, and record which version generated every returned trajectory.
Topologies
miles supports the first two topologies today. The single-endpoint external
rollout service is coming soon and extends that separation from GPU placement
to independent scaling, routing, and lifecycle.
--rollout-external is the second row, not the third. It prevents miles from
launching SGLang, but miles still knows the individual engine addresses, checks
their configuration, registers them with its router, and calls their
weight-update lifecycle.
An external rollout service is a narrower interface. miles sends rollout
requests to one endpoint and publishes new policy versions without needing an
engine handle for every replica. The service behind that endpoint can be
implemented by any deployment or control-plane package that satisfies the
request and policy-version contracts below.
Separate trainer and rollout GPUs
Disaggregated placement is the miles default. Give the trainer and rollout engines separate GPU counts and do not pass--colocate:
train_async.py. In a colocated job the two roles share
GPUs and take turns; fully async rollout therefore requires the disaggregated
layout. See Training Backends: Choosing the GPU layout
for placement and offload behavior.
To attach SGLang engines launched outside the miles Ray job, provide their
addresses explicitly:
--rollout-external does not hand off weight-update ownership:
miles still runs the selected weight-update lifecycle.
Weight synchronization
Once training and rollout use different GPUs, updated weights have to cross the boundary between them.--update-weight-transfer-mode selects the current
miles-managed path:
These are weight synchronization choices, not different rollout APIs. In the
first two modes, miles transfers tensors into known engine ranks directly.
P2P RDMA is an in-cluster transfer optimization; it is not the external rollout
service mechanism described later in this page. Disk-delta instead establishes
a versioned publication boundary that can also be consumed by an external
rollout system.
Disk-delta publication and activation
Enable disk-delta on a non-colocated Megatron run with a publication directory visible to the trainer and rollout hosts, plus a host-local checkpoint directory for each rollout host:- On the first
update_weights()call, miles captures a CPU snapshot from--hf-checkpoint. No delta version is published. Each rollout host also materializes the same base checkpoint in its local directory. - At the next update boundary, source trainer ranks gather Megatron tensors under their canonical Hugging Face names and compare their bytes with the previous snapshot.
- miles publishes
weight_vNNNNNN/with compressed changed bytes and an index containing the version, base version, delta encoding, and final-state checksums. Files are written atomically before the version is consumed. - Each rollout host pulls the version and patches its host-local checkpoint. Delta application verifies the checksum of the resulting tensor, not only the transferred delta.
- miles pauses generation, reloads the materialized checkpoint into SGLang, advances the engine weight version, and resumes generation.
overwrite stores
changed positions and their new values; it is larger but idempotent. Both
encodings are byte-oriented: the base and exported policy must agree on tensor
names, dtypes, shapes, and byte layout.
Each published tensor carries a checksum of its complete target state.
xxh3-128 is the default; blake3 and adler32 are also accepted. A lineage,
layout, or checksum mismatch fails the update instead of activating a partially
updated policy.
On a POSIX shared filesystem, the published version becomes visible through
the normal filesystem contract. Object-store-backed mounts can use
--custom-update-weight-post-write-path on the miles side and
--sglang-custom-pull-weights-pre-read-hook on the SGLang side to make writes
visible before a rollout host reads them.
The maintained end-to-end coverage is
tests/e2e/megatron/test_qwen3_4B_disk_delta.py.
It exercises a Qwen3-4B Megatron trainer and two SGLang rollout engines on a
single 8-GPU node. The same storage contract supports separate hosts, but the
registered test does not reproduce a cross-cluster deployment.
Current main rejects disk-delta with --colocate, LoRA, or PD
disaggregation. It also requires --hf-checkpoint to be a local checkpoint
directory. The implementation is selected by the Megatron actor; it is not a
general FSDP weight-update path.
External rollout service contract
The coming single-endpoint integration builds on the current disk-delta publication path and removes the need for miles to hold one handle per rollout engine. The commands above cover miles-managed and attached-engine deployments; this section defines the external-service boundary. The intended boundary has two independent data paths: miles does not need to know how many replicas are behind the endpoint, where they run, or how the service replaces them. The integration is defined by ownership:
The rollout endpoint and the version store are separate interfaces. The
request path should not have to carry model-sized weights, and publishing a new
version should not require miles to enumerate the current replicas.
One open-source package implementing the rollout-service side is
Stitch. It connects miles policy
publication and version-constrained requests to an independently managed
rollout fleet. The miles contract remains package-agnostic.
Stitch pins the miles revision it integrates against;
Miles fork
records the trainer-side commits that pin depends on, how the same ownership
split lands in code, and how to move the pin forward.
Policy-version requirements
An external rollout request needs more than an inference payload. It must be able to express the policy version the caller can accept:- A minimum version permits any replica at or above the requested version. This bounds staleness while allowing the fleet to converge gradually.
- An exact version requires the replica to serve one particular policy. It is useful for reproducibility, evaluation, and update validation.
Publication and replica convergence
A policy version becomes eligible for rollout only after its complete artifact is visible. The publication contract therefore needs:- an immutable version identifier and its base lineage;
- atomic visibility of the completed version;
- final-state checksums for every changed tensor;
- a way for a new or restarted replica to materialize a valid base and catch up through later versions; and
- an activation acknowledgement before a replica advertises the new version.
Fully async rollout
Disaggregation and fully async rollout answer different questions:- Disaggregation decides where training and rollout run and who owns the rollout engines.
- Fully Async RL decides how generation, buffering, and optimizer steps overlap.
main
does not overlap an opaque external publication with that active generation
call.
An external rollout service allows policy publication and replica preparation
to proceed without draining the entire fleet. Only activation needs a short
engine-local pause. Different replicas may converge at different times, so
request constraints and served-version attribution become part of the RL data
contract rather than optional serving metadata.
miles already uses weight versions to measure fully async sample staleness. A
service integration must preserve that information so
--max-weight-staleness and custom data-buffer policies make decisions from
the policy that generated each sample.
What to measure
End-to-endupdate_weights time is not enough for a disaggregated service. It
mixes phases with different effects on rollout availability:
For current disk-delta runs, miles records
perf/update_weights_density and perf/update_weights_wire_bytes in addition
to the normal weight-update timing.
The open-source Stitch integration reports the following reference rollout-side
weight update timings:
Preparation does not pause the engine: rollout generation continues while the
weight delta is applied and the next weights are prepared. This makes staged
weight updates a natural fit for fully async training, because only activation
requires a brief engine pause.
Related guides
- Fully Async RL
- Training Backends
- P2P Weight Transfer
- PD Disaggregation
- Stitch: open-source disaggregated rollout service

