# Monarch, a single controller for distributed PyTorch

> Monarch is Meta's distributed programming framework for PyTorch, built in Rust and driven from Python, with remote actors, supervision trees for fault tolerance, point-to-point RDMA transfers and distributed tensors. Its hardest part is not the actor model but the build, which is why the tensor engine can be switched off entirely.

**meta-pytorch/monarch** — PyTorch Single Controller

- Repository: https://github.com/meta-pytorch/monarch
- Website: https://meta-pytorch.org/monarch
- Stars: 1,081 · Forks: 179
- Language: Rust
- License: BSD-3-Clause
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/meta-pytorch-monarch

## Four things in one controller

Monarch is a distributed programming framework for PyTorch based on scalable actor messaging, and the project description lists what it provides in four items.

Remote actors with scalable messaging. Actors are grouped into collections called meshes, and messages can be broadcast to all members.

Fault tolerance through supervision trees. Actors and processes form a tree, and failures propagate up the tree, which provides good default error behaviour and enables fine-grained fault recovery.

Point-to-point RDMA transfers. Cheap registration of any GPU or CPU memory in a process, with one-sided transfers based on libibverbs.

Distributed tensors. Actors can work with tensor objects sharded across processes.

The framing is that this is a single controller library, and the name in the package description is PyTorch Single Controller. That is a claim about the shape of the system: one Python process decides where processes go and who talks to whom, and the distributed work happens underneath.

The supervision tree is the piece with the most design content. A flat model, where any actor failure fails the job, is easy to reason about and useless at scale. A tree lets a supervisor restart a subtree without taking down the rest, and the project describes that as good default error behaviour with fine-grained recovery on top.

The RDMA piece is the one that differentiates it from a messaging library on a cluster without RDMA hardware, because the transfers are one-sided and the memory registration is cheap, which is what makes it plausible to move tensors rather than messages.

The primary language is Rust, the licence is BSD-3-Clause, and the homepage is meta-pytorch.org/monarch.

## The API is imperative Python over a Rust runtime

The example in the project description is short, and it shows the style of the API.

```python
from monarch.actor import Actor, endpoint, this_host

# spawn 8 trainer processes one for each gpu
training_procs = this_host().spawn_procs({"gpus": 8})


# define the actor to run on each process
class Trainer(Actor):
    @endpoint
    def train(self, step: int): ...


# create the trainers
trainers = training_procs.spawn("trainers", Trainer)

# tell all the trainers to take a step
fut = trainers.train.call(step=0)

# wait for all trainers to complete
fut.get()
```

Monarch code imperatively describes how to create processes and actors using a simple Python API, and the flow is process placement first, actor class second, spawn third, call fourth.

The call on a mesh of actors returns a future, and the future is how a distributed result comes back to the controller. The mesh handles the broadcast, so the trainer method is written once and the runtime takes care of running it on eight GPUs.

Two details in the API are worth reading twice. The endpoint decorator marks which methods of an actor are callable from outside, which keeps the actor class a plain class with a small public surface. And the call is a method on the mesh object rather than on a single actor, which is the difference between a framework where you write a loop over ranks and one where the runtime owns the loop.

The process placement is expressed as a resource request, here a count of GPUs, and this_host is the host you are on. The combination of a placement request and an actor class is the whole model in two lines.

## Prebuilt wheels, and why the torch version is not your problem

The installation section leads with pre-built wheels that work regardless of what version of PyTorch you have installed, and you can get them with pip or with uv. For the pinned nightly examples, you replace YYYYMMDD with the build date.

```bash
# stable
pip install torchmonarch
# nightly
pip install --pre torchmonarch
# specific
pip install torchmonarch==0.8.0.devYYYYMMDD
```

The uv equivalents add the package to the project properly rather than to the environment, with uv add for stable, uv add with the prerelease allowed flag for nightly, and the same pinned form for a specific build.

The claim that the wheels work regardless of the installed torch version is the useful one, because a native extension normally does not have that property. The build configuration in the project metadata explains what is going on. The build system requires setuptools, setuptools-rust, wheel, numpy and torch, and the comment says torch is required at build time, not just at runtime, for three reasons: setup.py needs to detect the torch installation paths and the C++11 ABI, the C++ extensions link against libtorch, and the build time torch detection determines whether the tensor_engine feature is enabled.

So the native code is compiled against whatever torch is present, and the version compatibility problem is handled at build time rather than by pinning a torch version for you.

The nightly line is where the interesting work is, given the release history. The tagged releases are v0.7.0 from 2026-10-10, v0.6.0 from 2026-07-15 and v0.5.0 from 2026-05-19, and the nightly version format in the install command is 0.8.0.dev with a build date, so the 0.8 line is what the nightly wheels carry.

## The tensor engine switch, and what you lose when you turn it off

Monarch includes distributed tensor and RDMA APIs, and the build is layered so that you can leave the GPU parts out. The tensor engine builds on any platform, including CPU-only hosts and macOS, while the GPU-specific pieces, NCCL, RDMA and rdmaxcel, layer on top and only build on Linux with a CUDA or ROCm toolchain installed.

If you want a lighter-weight version of Monarch, actors only with no torch dependency, you set USE_TENSOR_ENGINE=0.

```sh
USE_TENSOR_ENGINE=0 uv sync
```

The project is direct about the consequence. Building without tensor_engine means you will not have access to the distributed tensor or RDMA APIs. Torch is required to use tensor_engine, and the latest stable torch is ABI compatible with the latest versioned torchmonarch.

So the switch is not a performance option, it is a scope option. What remains is the actor system, the messaging and the supervision trees, which is a coherent thing to want, and the reason to set it is that an actors-only build does not need a GPU toolchain, does not need libibverbs and does not need torch at all.

That makes the framework usable in a CI job on a laptop, which is the practical reason the switch exists. A cluster project that only needs to test the actor logic does not need a GPU node to do it.

The build also has two Dockerfiles, a Dockerfile and a Dockerfile.nightly, which is the same distinction expressed for the container path.

## MONARCH_GPU_PLATFORM, and the CUDA index you have to think about

The MONARCH_GPU_PLATFORM environment variable controls which GPU libraries the build links against, and it accepts three values: cuda for CUDA with NCCL and RDMA, rocm for ROCm, and none to force a CPU-only tensor engine even on a host where CUDA or ROCm is installed.

Leaving it unset auto-detects whichever toolchain is present. Setting it explicitly is required when both CUDA and ROCm are installed, and none is the explicit opt-out when you want the CPU tensor engine on a GPU-capable host.

```sh
# Force a CPU-only tensor engine (no CUDA/ROCm/RDMA libraries required)
MONARCH_GPU_PLATFORM=none uv sync

# Force CUDA on a host that also has ROCm
MONARCH_GPU_PLATFORM=cuda uv sync
```

The second issue is the PyTorch index. By default, Monarch builds with PyTorch from the pytorch-cu132 index, CUDA 13.2, and to use a different CUDA version you either edit the tool.uv.sources section in pyproject.toml to point at a different index, such as pytorch-cu130 or pytorch-cpu, or use the extra index url option when running uv.

```sh
uv sync --extra-index-url https://download.pytorch.org/whl/cu130
```

Both mechanisms are needed because the choice is a function of two things, the GPU stack on the machine and the torch build you want to link against, and neither is something the tool can guess reliably. A machine with both CUDA and ROCm is common enough that the project makes the platform explicit rather than automatic.

The Dockerfile makes the same choice in a different way, with a build argument for the PyTorch tag defaulting to a CUDA 13.2 runtime image.

## Building from source, and the dependency list it needs

Building from source requires additional system dependencies, and the project is clear that they are needed at build time only, not at runtime.

The Rust toolchain is a nightly, installed through rustup, and the default is set to nightly. The system libraries are listed per distribution, and the Fedora list gives a clear picture of the shape: cmake, ninja-build, protobuf-compiler and libunwind, then the CUDA toolkit matching the machine, cuda-toolkit-13-2 and cuda-13-2 in the example, then clang-devel, nccl-devel and libstdc++-static, then the RDMA libraries libibverbs, rdma-core, libmlx5, libibverbs-devel and rdma-core-devel, which the project marks as needed for tensor_engine builds.

The Ubuntu instructions are the same set with the Ubuntu package names, libunwind-dev instead of libunwind and the RDMA packages as rdma-core, libibverbs1, libmlx5-1 and libibverbs-dev. They also set clang as the default C and C++ compiler through the CC and CXX environment variables, which is a detail that saves a confusing build failure on a machine where gcc is the default.

After the clone, the development install is a uv sync, and there is a verification command.

```sh
uv run python -c "from monarch import actor; print('Monarch installed successfully')"
```

A Rust change is rebuilt with uv pip install -e ., and the same USE_TENSOR_ENGINE=0 switch applies to the rebuild, so the actors-only path is a first class way to work on the repository rather than a fallback.

## A Rust workspace with a large crate surface

The repository is a Cargo workspace with an unusually large number of crates, and their names describe the system.

The hyperactor family is the core, with crates for the actor system, its cast machinery, configuration and configuration macros, the mesh abstraction, mesh macros, a mesh admin TUI, a remote transport, telemetry and the macro crates for the whole thing. The monarch crates are the Python facing layer, with monarch_hyperactor as the bridge, plus monarch_rdma, monarch_tensor_worker, monarch_types, monarch_telemetry_schema, monarch_distributed_telemetry, monarch_record_batch and its macros, monarch_mini, monarch_gil, monarch_perfetto_trace, monarch_introspection_snapshot, monarch_extension, monarch_conda, monarch_bench and monarch_cpp_static_libs.

There are also the algebra crate, the chrysalis crate, the ndslice crate and the nccl-sys crate, and directories for deps, build_utils, ci, docs, examples, erased_lifetime and the build plugin for cargo.

The examples directory shows the intended use cases, with a hello_gather_mount example, example_actors, kubernetes, skypilot, a remotemount example, an rdma_pingpong example, a presentation directory, and three Slurm notebooks, slurm_allreduce, slurm_ddp and slurm_titan, plus a torchtitan_mast directory. The Slurm notebooks and the example directories are a reasonable summary of who this is for: large scale training on a cluster.

The Cargo manifest patches the entire arrow family to a specific git revision of arrow-rs rather than using the crates.io release, which is the sort of thing a project does when it needs a fix that is not upstream yet, and it is worth knowing about before you depend on the build.

The repository also carries a CITATION.cff, a MONARCH_INFO.md, a SECURITY.md, a CODE_OF_CONDUCT.md and a clippy.toml.

## Conclusion

Adopt Monarch if you are running distributed PyTorch training and want a single controller that owns process placement, actor messaging and fault handling rather than a launcher and a distributed runtime bolted together, and you have the hardware and the appetite for a Rust and Python build. Do not adopt it if you need the GPU tensor and RDMA stack on hosts without a CUDA or ROCm toolchain, because those pieces only build on Linux, and the lighter build with USE_TENSOR_ENGINE=0 removes the tensor and RDMA APIs rather than emulating them. Before you start, pick the GPU platform explicitly if a host has both CUDA and ROCm, and read the supervision tree documentation before you rely on the fault recovery behaviour.

## FAQ

### What does Monarch provide for distributed PyTorch?

Remote actors with scalable messaging through meshes and broadcast, fault tolerance through supervision trees where failures propagate up the tree, point-to-point RDMA transfers based on libibverbs, and distributed tensors sharded across processes.

### How do I install Monarch?

Prebuilt wheels work regardless of the installed PyTorch version, with `pip install torchmonarch` for stable, `pip install --pre torchmonarch` for nightly, and a pinned form for a specific build. The uv equivalents are `uv add torchmonarch` and `uv add --prerelease=allow torchmonarch`.

### What does USE_TENSOR_ENGINE=0 do?

It builds a lighter-weight version of Monarch with actors only and no torch dependency, on any platform including CPU-only hosts and macOS. The distributed tensor and RDMA APIs are not available in that build, and the GPU specific pieces only build on Linux with a CUDA or ROCm toolchain anyway.

### How do I choose between CUDA and ROCm when building?

The MONARCH_GPU_PLATFORM environment variable takes cuda, rocm or none. Leaving it unset auto-detects, but you must set it explicitly when a host has both CUDA and ROCm installed, and none forces the CPU-only tensor engine on a GPU-capable host.

### What is the status and licence of Monarch?

The project is BSD-3-Clause licensed, classified as Development Status 4 - Beta, and requires Python 3.10 or newer. The recent releases are v0.7.0 from 2026-10-10, v0.6.0 from 2026-07-15 and v0.5.0 from 2026-05-19, and the last push was on 2026-10-19.

## Sources

- [License: BSD-3-Clause](https://github.com/meta-pytorch/monarch/blob/main/LICENSE)
- [meta-pytorch/monarch on GitHub](https://github.com/meta-pytorch/monarch)
- [Project website](https://meta-pytorch.org/monarch)
- [README](https://github.com/meta-pytorch/monarch/blob/main/README.md)
- [Releases](https://github.com/meta-pytorch/monarch/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/meta-pytorch-monarch
