> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.stereolabs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.stereolabs.com/_mcp/server.

# Create and use Docker images for ZED and ROS 2

This guide is based on the [`docker` folder of the ZED ROS 2 Wrapper repository](https://github.com/stereolabs/zed-ros2-wrapper/tree/master/docker), which builds Docker images that run the *ZED ROS 2 Wrapper*.

The images are built **on top of the official [StereoLabs ZED images](https://hub.docker.com/r/stereolabs/zed/tags)**, which already ship CUDA and the ZED SDK. On top of that base, the build installs ROS 2 and compiles the wrapper from the sources of the current checkout.

A single, parameterized `Dockerfile` is used for every target. Two entry-point scripts select the right StereoLabs base image and pass the build arguments:

* `build_desktop.sh`: desktop / dGPU images (CUDA + Ubuntu base, using the `gl-devel` variant).
* `build_jetson.sh`: NVIDIA® Jetson™ images (L4T base, using the `devel` variant).

Everything else these scripts rely on lives in the `scripts/` folder and is not meant to be run directly:

| File                             | Role                                                                                                          |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `Dockerfile`                     | Single image definition, parameterized by `BASE_IMAGE`, `ROS2_DISTRO`, and (optionally) `CUSTOM_ZED_SDK_URL`. |
| `scripts/install_ros2.sh`        | Installs ROS 2 (from APT, or from source if no binaries exist for the base OS).                               |
| `scripts/install_zed_sdk.sh`     | Installs the ZED SDK from a custom `.run` URL (only when `--sdk-url` is used).                                |
| `scripts/build_wrapper.sh`       | Installs the wrapper dependencies and builds the wrapper with `colcon`.                                       |
| `scripts/zed-wrapper-deps.repos` | Source-mode wrapper dependencies (used only when ROS 2 is built from source).                                 |
| `scripts/ros_entrypoint.sh`      | Container entry point (sets up the ROS 2 environment).                                                        |
| `scripts/_common.sh`             | Shared helpers for the build scripts.                                                                         |

> **Note**
>
> The supported **ROS 2 distributions**, ZED SDK versions, and the CUDA/Ubuntu and JetPack/L4T combinations that are known to build and run change with every release. For the complete, up-to-date list of **available and validated configurations**, as well as the default values for each build option, always refer to the [`docker` folder of the ZED ROS 2 Wrapper repository](https://github.com/stereolabs/zed-ros2-wrapper/tree/master/docker).

> **Note**
>
> The entry point sets `ROS_DOMAIN_ID` to `0`, the default value in ROS 2. If your setup requires a different value, change it in `scripts/ros_entrypoint.sh` before building your image, or run `export ROS_DOMAIN_ID=<new_value>` at the start of each interactive session. See the [official ROS 2 documentation](https://docs.ros.org/en/rolling/Concepts/Intermediate/About-Domain-ID.html#the-ros-domain-id) for details.

## ROS 2 installation: APT or from source

The image installs ROS 2 **from APT when binary packages exist** for the selected distribution on the base image's Ubuntu release, and **builds ROS 2 from source otherwise**. The decision is made automatically during the build by reading the Ubuntu codename of the base image.

> **Warning**
>
> **From-source builds are heavy and best-effort.** Building ROS 2 from source compiles the whole ROS 2 core and can take a long time. Each ROS 2 distribution also officially targets a specific Ubuntu release, so a from-source build on a different release is not guaranteed to compile.

## Build the Docker images

First, check out the tag or commit of the wrapper you want to build:

```bash
git checkout <branch_or_tag>
```

### Desktop

```bash
./build_desktop.sh [--ros-distro <ros2_distro>] [--os ubuntu-<XX.XX>] \
                   [--sdk <X.Y.Z|latest>] [--cuda <XX.X>] [--sdk-url <URL>]
```

Run it without arguments to build with the default configuration. Desktop images always use the StereoLabs `gl-devel` base variant.

### Jetson

```bash
./build_jetson.sh [--ros-distro <ros2_distro>] [--os <jpX.Y.Z|l4t-rXX.X>] \
                  [--sdk <X.Y.Z|latest>] [--sdk-url <URL>]
```

Run it without arguments to build with the default configuration. Jetson images always use the StereoLabs `devel` base variant.

### Notes on inputs

* **`--ros-distro`** selects the ROS 2 distribution to install. It must be supported on the base image's Ubuntu release, otherwise the script fails fast before building.
* **`--sdk latest`** resolves to the newest ZED SDK that has an image for the chosen platform on Docker Hub. Some platforms may not yet have an image for the very latest SDK, in which case `latest` resolves to the most recent SDK published for that platform.
* **`--sdk-url <URL>`** builds against a custom or unreleased ZED SDK `.run` installer instead of a published image; see [Build against a custom or unreleased ZED SDK](#build-against-a-custom-or-unreleased-zed-sdk).
* The base image tag is validated against Docker Hub before the build. If a combination does not exist, the script stops with a clear message. Check the available tags on the [StereoLabs Docker Hub](https://hub.docker.com/r/stereolabs/zed/tags) page.

## Build against a custom or unreleased ZED SDK

By default, the image is built on a `stereolabs/zed` base that already ships a **published** ZED SDK version. To build against a ZED SDK that is **not** published as a Docker image (a local build or a pre-release `.run` installer), pass it to `--sdk-url`, either as a **URL** or as a path to a **local `.run` file**:

```bash
# Desktop: plain CUDA base + custom SDK installer.
# --cuda must be a FULL version (X.Y.Z) to select an nvcr.io/nvidia/cuda tag.
./build_desktop.sh --ros-distro <ros2_distro> --os ubuntu-<XX.XX> --cuda <X.Y.Z> \
                   --sdk-url <URL_or_local_path_to_ZED_SDK.run>

# Jetson: plain L4T JetPack base + custom SDK installer.
# --os must be a FULL L4T version (l4t-rX.Y.Z) to select an nvcr.io/nvidia/l4t-jetpack tag.
./build_jetson.sh --ros-distro <ros2_distro> --os l4t-r<X.Y.Z> \
                  --sdk-url <URL_or_local_path_to_ZED_SDK.run>
```

When `--sdk-url` is set:

* The base image is a **plain NVIDIA image** (`nvcr.io/nvidia/cuda` for desktop, `nvcr.io/nvidia/l4t-jetpack` for Jetson) instead of a `stereolabs/zed` image, and the SDK is installed inside the image by `scripts/install_zed_sdk.sh`.
* `--sdk` is ignored, and no Docker Hub tag lookup is done. The full version is required (`--cuda X.Y.Z` on desktop, `--os l4t-rX.Y.Z` on Jetson) because the NVIDIA base image tag is derived from it; make sure that tag exists on [nvcr.io](https://catalog.ngc.nvidia.com/).
* If `--sdk-url` is a **path to an existing local file**, it is staged into the build context and copied in directly (no network fetch). Otherwise it is treated as a **URL**: it must return HTTP 200 (checked during the build) and point to a ZED SDK Linux `.run` installer matching the base CUDA/L4T and Ubuntu version.

## Cross compilation

You can build a Jetson (`arm64`) image from an `x86_64` desktop using QEMU emulation.

1. Register the QEMU `binfmt` handlers so the host can run `arm64` containers (run once per host; repeat after a reboot):

   ```bash
   docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
   ```

2. *(Optional)* Check that `arm64` emulation works; this should print `aarch64`:

   ```bash
   docker run --rm --platform linux/arm64 arm64v8/ubuntu uname -m
   ```

3. Build the Jetson image, forcing the `arm64` platform. `build_jetson.sh` calls `docker build` without `--platform`, so on an `x86_64` host export `DOCKER_DEFAULT_PLATFORM` to make the (arm64-only) base image and every `RUN` step run under emulation:

   ```bash
   DOCKER_DEFAULT_PLATFORM=linux/arm64 ./build_jetson.sh [options]
   ```

> **Warning**
>
> **From-source builds under emulation are slow.** A distribution with no APT binaries for its base Ubuntu release builds the whole ROS 2 core from source, which can take a couple of hours under QEMU. For a fast, binary (APT) build under emulation, pick a distribution whose target Ubuntu matches the base. Building natively on the Jetson™ itself needs neither QEMU nor `DOCKER_DEFAULT_PLATFORM` and avoids this overhead entirely.

## Run the Docker image

**NVIDIA runtime**

The NVIDIA® drivers must be accessible from the container to run the ZED SDK on the GPU. You will need:

* The `nvidia` container runtime installed and registered with Docker, following [this guide](/docs/integrations/docker/install-on-linux#nvidia-docker).
* GPU access, set with `--runtime nvidia` (or `--gpus all` on desktop), together with `-e NVIDIA_DRIVER_CAPABILITIES=all` to expose all the driver capabilities (compute, video, graphics).
* Docker privileged mode, set with `--privileged`.

**Network**

Set up the network configuration to enable communication between the container, other containers, and the host:

* `--network=host`: remove network isolation between the container and the Docker host.
* `--ipc=host`: use the host system's Inter-Process Communication namespace.
* `--pid=host`: use the host system's process ID namespace.

**Display context for CUDA-based applications**

Share the host `DISPLAY` with the container using `-e DISPLAY=$DISPLAY`.

> **Note**
>
> The shared volume `/tmp/.X11-unix/:/tmp/.X11-unix` is also required.

**Shared volumes**

A few volumes should also be shared with the host:

* `/tmp/.X11-unix/:/tmp/.X11-unix`: required to enable X11 server communication for CUDA-based applications.
* `/usr/local/zed/settings:/usr/local/zed/settings`: required if you plan to use the robot in an area without internet access and you previously downloaded the camera calibration files by following [this guide](https://support.stereolabs.com/hc/en-us/articles/21614848880791-How-can-I-use-the-ZED-with-Docker-on-a-robot-with-no-internet-connection).
* `/usr/local/zed/resources:/usr/local/zed/resources`: recommended to persist the ZED SDK AI models (Object Detection, Body Tracking, NEURAL depth) across runs. Use a different host folder per SDK version.
* `/dev:/dev`: shares the video and other required devices.
* `/dev/shm:/dev/shm`: required to use ROS 2 with shared memory.
* For [GMSL2](/docs/products/embedded/zed-link-capture-card/gmsl2#what-is-gmsl2) cameras (ZED X, ZED X One), you will also need:
  * `/tmp:/tmp`
  * `/var/nvidia/nvcam/settings/:/var/nvidia/nvcam/settings/`
  * `/etc/systemd/system/zed_x_daemon.service:/etc/systemd/system/zed_x_daemon.service`

**Start the container**

First, allow the container to access EGL display resources (required once per X session, for example after each reboot or login):

```bash
sudo xhost +si:localuser:root
```

Then start an interactive session:

**USB3 cameras**

```bash
docker run --runtime nvidia -it --privileged --network=host --ipc=host --pid=host \
  -e NVIDIA_DRIVER_CAPABILITIES=all -e DISPLAY=$DISPLAY \
  -v /tmp/.X11-unix/:/tmp/.X11-unix \
  -v /dev:/dev \
  -v /dev/shm:/dev/shm \
  -v /usr/local/zed/resources/:/usr/local/zed/resources/ \
  -v /usr/local/zed/settings/:/usr/local/zed/settings/ \
  <docker_image_tag>
```

**GMSL2 cameras**

```bash
docker run --runtime nvidia -it --privileged --network=host --ipc=host --pid=host \
  -e NVIDIA_DRIVER_CAPABILITIES=all -e DISPLAY=$DISPLAY \
  -v /tmp:/tmp \
  -v /dev:/dev \
  -v /var/nvidia/nvcam/settings/:/var/nvidia/nvcam/settings/ \
  -v /etc/systemd/system/zed_x_daemon.service:/etc/systemd/system/zed_x_daemon.service \
  -v /usr/local/zed/resources/:/usr/local/zed/resources/ \
  -v /usr/local/zed/settings/:/usr/local/zed/settings/ \
  <docker_image_tag>
```

Once the container is running, see [Using ZED with ROS 2 from a Docker Container](/docs/integrations/docker/using-ros-in-docker) and the [ROS 2 documentation](/docs/integrations/ros-2/) to launch the ZED nodes.