> ## Documentation Index
> Fetch the complete documentation index at: https://aegean.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Working on a Mac

> Setting up PyTorch and ROS 2 on an Apple Silicon Mac, natively or in a container.

Mac users run into two separate issues with the course containers. First, Docker runs inside a Linux virtual machine and cannot reach Apple's Metal framework, so PyTorch uses the CPU even on an M-series Mac. Second, graphical ROS tools such as Gazebo and RViz2 need XQuartz to display their windows. This page walks through the practical options for both cases. For general container setup, see [Docker containers](/aiml-common/resources/environment/docker-containers).

## PyTorch native install

Installing PyTorch directly on macOS lets it use the GPU through Apple's [Metal Performance Shaders](https://developer.apple.com/metal/pytorch/) backend. PyTorch calls this device `mps`.

### Setup

You need Python and `uv` for this setup. If either is missing, start with the [Python and uv guide](/aiml-common/resources/environment/python-uv). Once `uv` works in your terminal, clone the repository for your course and install its dependencies:

```bash theme={null}
# Import your class repository to your own account, then clone it.
# Your assignment page links to the repository for your course.
git clone https://github.com/<your-username>/<class-repo>.git
cd <class-repo>

# Create the environment and install the project
uv venv
uv sync
```

The standard Apple Silicon build of PyTorch already includes Metal support, so there is no extra GPU package to install. You also do not need CUDA; CUDA is for NVIDIA GPUs.

### Selecting the device

It is better to detect the available device than to hard-code one. This small check works on an Apple Silicon Mac, a CUDA machine, and Colab:

```python theme={null}
import torch

if torch.cuda.is_available():
    device = torch.device("cuda")
elif torch.backends.mps.is_available():
    device = torch.device("mps")
else:
    device = torch.device("cpu")

print(f"torch {torch.__version__} on {device}")
```

On an M-series Mac, the result should be `mps`. If you see `cpu`, check whether you are running inside a container or using a PyTorch build without Metal support.

### When an operation is not supported

The Metal backend does not yet support every PyTorch operation. If your model uses one of them, PyTorch stops and names the unsupported operator. You can allow that operation to fall back to the CPU:

```bash theme={null}
export PYTORCH_ENABLE_MPS_FALLBACK=1
```

Add the variable to your shell profile if you need it regularly. A model with many fallbacks may still be slow because it repeatedly moves tensors between the GPU and CPU. See the [MPS backend documentation](https://docs.pytorch.org/docs/stable/notes/mps.html) for current details.

### A note about course notebooks

Some course notebooks check for `cuda` but not `mps`, so they may still use the CPU after you finish this setup. Replacing their device-selection code with the example above is usually enough to fix the problem. If you find one, please tell us in Discord so we can correct the notebook as well.

## Robotics on a Mac

For robotics coursework, start with the [ROS container](#ros-container). ROS 2 does not officially support macOS, and the project does not publish macOS binaries. The container avoids that problem by running ROS 2 on Linux, the platform the course is built and tested on.

If the container does not work well on your Mac, you have three alternatives. An Ubuntu virtual machine most closely follows the course book, RoboStack provides prebuilt Jazzy packages for Apple Silicon, and a community Homebrew tap offers a native installation. We have not tested these alternatives ourselves, so expect to do some troubleshooting if you choose one.

```mermaid theme={null}
graph TB
    Start["Robotics on an Apple Silicon Mac"]

    Start --> Docker["ROS container<br/>docker plus XQuartz for RViz and Gazebo"]
    Start --> VM["Ubuntu 24.04 virtual machine<br/>ARM64 image, software-rendered Gazebo"]
    Start --> Robo["RoboStack<br/>conda and pixi, no container"]
    Start --> Brew["Homebrew tap<br/>no container, no XQuartz"]

    Docker --> DockerS["Supported course setup"]
    VM --> VMS["Closest to the book"]
    Robo --> RoboS["Native Apple Silicon packages"]
    Brew --> BrewS["Community maintained and untested"]

    classDef root fill:#37474f,color:#fff,stroke:#546e7a
    classDef option fill:#0277bd,color:#fff,stroke:#01579b
    classDef supported fill:#2e7d32,color:#fff,stroke:#1b5e20
    classDef untested fill:#e65100,color:#fff,stroke:#bf360c

    class Start root
    class Docker,VM,Robo,Brew option
    class DockerS,VMS,RoboS supported
    class BrewS untested
```

### ROS container

This is the setup we support for the course. The robotics work lives in the [turtlebot-maze](https://github.com/pantelis/turtlebot-maze) repository, which is also listed under [Starter repos](/aiml-common/resources/environment/starter-repos). The repository includes its own Compose file. From its root directory, run:

```bash theme={null}
docker compose build
docker compose up
```

RViz2 and Gazebo need [XQuartz](https://www.xquartz.org/) to display their windows on macOS:

```bash theme={null}
brew install --cask xquartz
```

After installing, open XQuartz, go to **Preferences > Security**, and enable **Allow connections from network clients**. Then allow connections:

```bash theme={null}
xhost +localhost
```

If you would rather avoid XQuartz, `turtlebot-maze` includes a `foxglove-bridge` service. It streams the same ROS topics to your browser over a WebSocket and does not use X11:

```bash theme={null}
docker compose up foxglove-bridge
```

Open `ws://localhost:8765` in [Foxglove](https://app.foxglove.dev), then load the layout from the repository's `foxglove/` directory. This is often the easier option on a Mac.

### Ubuntu VM

Assignment 1 uses Edouard Renard's *ROS 2 from Scratch*. In chapter 2, the book installs Ubuntu 24.04 in a virtual machine before adding ROS 2. Because ROS 2 Jazzy targets Ubuntu 24.04, following the book gives you an environment close to the one used for this course.

There are two important limitations. Be sure to download the **ARM64** build of Ubuntu; running the amd64 version through x86 emulation is much slower on an M-series Mac. The virtual machine also cannot use the Mac's GPU, so Gazebo relies on software rendering and may struggle with more complex worlds.

[UTM](https://mac.getutm.app) is a free, QEMU-based virtual machine app that runs ARM64 Linux on Apple Silicon. Its paid Mac App Store version is the same application and helps fund development; you do not need to buy it. VMware Fusion and Parallels are also suitable.

A virtual machine uses more disk space and memory than a container and takes longer to start. As assignment 1 suggests, stay with Docker if it is working well. The VM is a useful fallback when it is not.

### RoboStack

[RoboStack](https://robostack.github.io/) packages ROS for conda-style environments. Instead of installing ROS across your whole system, you create a separate environment for the project. RoboStack includes Jazzy, the ROS 2 distribution used in this course.

The `robostack-jazzy` channel includes Apple Silicon packages for `osx-arm64`, including `ros-jazzy-desktop`. A basic setup with Pixi looks like this:

```bash theme={null}
pixi init ros_ws -c https://prefix.dev/robostack-jazzy
cd ros_ws
pixi add ros-jazzy-desktop
pixi run rviz2
```

The main difficulty is adapting the coursework. `turtlebot-maze` assumes that you will use its Compose file. With RoboStack, you must configure and launch the nodes yourself instead of running `docker compose up`. That adds more ROS setup than the first assignment is meant to cover, but it can be a useful experiment after you have completed the assignment.

### Homebrew tap

<Warning>
  We have not tested this.
</Warning>

A community Homebrew tap provides [ROS 2 Jazzy for macOS](https://discourse.openrobotics.org/t/ros2-jazzy-is-now-available-on-homebrew/55737) without a container. It was announced in June 2026.

```bash theme={null}
brew tap idesign0/ros2
brew install ros2-jazzy
```

The tap includes Gazebo Harmonic (`gz-sim8`), `ros2_control`, MoveIt 2, and Nav2. Because these run directly on macOS, this approach avoids both Docker and XQuartz.

We have not tested this route. The tap is maintained by the community, and there is no official macOS release of ROS 2 to fall back on if something breaks. Choose it only if you are comfortable diagnosing installation problems yourself. The container remains the safer option because it runs ROS 2 on a supported Linux platform.

***

<Callout icon="pen-to-square" iconType="regular">
  [Edit this page on GitHub](https://github.com/aegean-ai/eaia/edit/main/src/aiml-common/resources/environment/working-on-a-mac.mdx) or [file an issue](https://github.com/aegean-ai/eaia/issues/new/choose).
</Callout>
