Multi-Camera Setup with Docker
Multi-Camera Setup with Docker
If several ZED cameras are connected to the same host, Docker can help you deploy and manage all of them, either from a single container or from one container per camera. This page walks through both architectures so you can pick the one that fits your application, then covers the additional setup needed to run several cameras in separate containers.
This page assumes you are already familiar with running a single ZED camera in Docker. If not, start with Install on Linux or Install on NVIDIA® Jetson™, and check the hardware recommendations for multi-camera rigs.
One container per camera, or one container for all?
Running each camera in its own container isn’t the only valid architecture, and container-level isolation isn’t free.
Multiple containers (one per camera)
Each camera is opened by its own instance of your application, running in its own container; there are as many containers as cameras, each started and managed independently.
Advantages
- Fault isolation: a crash or a stuck camera in one container doesn’t affect the others.
- Independent lifecycle: restart, update, or roll back one camera’s application without touching the rest, and apply per-camera resource limits (
--cpus,--memory, see the Docker run reference) individually, or through an orchestrator. - ROS 2 offers the same choice: composable nodes can be launched as separate processes instead of being composed together, for the same isolation and easier per-node debugging (see ROS 2 Composition). If you want that isolation for your ROS 2 nodes too, launch each camera’s node in its own process, in its own container.
Disadvantages
- Fixed overhead multiplies: each container is its own CUDA context and loads its own copy of the optimized AI/depth models into GPU memory. See Cache AI models and calibration files across containers below and the GPU memory guidance for multi-camera rigs.
- ROS 2 zero-copy IPC isn’t possible across containers: it only works between composable nodes loaded into the same ROS 2 component container, a single OS process, and a process can’t span two Docker containers. Camera nodes split across several containers fall back to regular (serialized) ROS 2 topic transport instead.
- ZED SDK Fusion has the same constraint: its low-latency
INTRA_PROCESSworkflow requires the camera publishers and the Fusion subscriber to run in the same process. Cameras split across containers must use itsLOCAL_NETWORKworkflow instead, at the cost of a small (<5ms) network hop; see the configuration file reference for both modes.
Single container (all cameras in one process)
A single instance of your application, running in one container, opens and manages every camera itself, with one process handling all of them at once.
Advantages
- Shared GPU resources: one CUDA context and one set of loaded models serve every camera in that process, instead of one per camera.
- ROS 2 zero-copy IPC: required for camera nodes to communicate with each other, or with a separate consumer node, without copying data (see the ZED IPC tutorial). This is in fact the default: the ROS 2 multi-camera launch file already loads every camera node as a component in the same ROS 2 container, specifically so that IPC remains available if you add a consumer node later.
- ZED SDK Fusion’s
INTRA_PROCESSshared-memory path: needs every camera in the same process too; it’s the lowest-latency of Fusion’s two workflows, documented in the configuration file reference. - Fewer moving parts: fewer containers and processes to start, monitor, and network together.
Disadvantages
- No fault isolation: a crash or a stuck camera can take down every camera hosted in that process.
- Coupled restarts: restarting or updating one camera’s logic means restarting the whole container, interrupting every other camera it hosts.
- Shared limits and image: all cameras share the same container-level resource limits and the same base image and library versions; you cannot give one camera more CPU, or run it against a different ZED SDK version, without affecting the rest.
These aren’t all-or-nothing: if only some of your cameras need to share data (for Fusion or ROS 2 IPC), you can group just those cameras into one container/process and keep the rest isolated in their own containers.
The rest of this page walks through the practical setup for both: starting with a single container that opens every camera itself, then the additional steps needed to split cameras across several containers instead.
Setting up a multi-container configuration
Getting several containers to each own one camera correctly means sharing the same volumes across all of them, avoiding duplicate AI-model optimization, and assigning a specific camera to each container.
Share the same volumes with every container
All the ZED cameras connected to a host, whether USB or GMSL2, are exposed through the same host devices and services. Docker cannot restrict a container to a single camera’s device node, so every container needs the same runtime options and volumes as a single-camera setup:
- USB cameras:
-v /dev/:/dev/ - GMSL2 cameras (ZED X, ZED X Mini, ZED X Nano, ZED X One):
-v /dev/:/dev/,-v /tmp/:/tmp/,-v /var/nvidia/nvcam/settings/:/var/nvidia/nvcam/settings/, and-v /etc/systemd/system/zed_x_daemon.service:/etc/systemd/system/zed_x_daemon.service
The ZED Link driver (for GMSL2 cameras) must only be installed on the host, never inside a container.
Cache AI models and calibration files across containers
By default, every time a fresh container opens a camera it may need to:
- Optimize the NEURAL depth model for the current GPU. This optimized engine is tied to the GPU and resolution, not to an individual camera, but if it isn’t persisted it will be rebuilt on every container restart, which can take a few minutes, especially on embedded platforms.
- Download the camera’s calibration file from StereoLabs servers, if it isn’t already cached locally.
Bind-mount the following host folders into every container so this work happens only once and is shared by all of them, instead of being repeated per container:
/usr/local/zed/resourcesstores the optimized AI models (see the Volumes section for other uses of this folder)./usr/local/zed/settingsstores each camera’s calibration file (SN<serial>.conf).
Mounting the host’s own /usr/local/zed/resources and /usr/local/zed/settings directories (rather than an empty folder) lets every container reuse whatever has already been optimized or downloaded on the host.
If several containers start at the same time and the AI models aren’t already optimized in the shared volume, every one of them will try to optimize the models simultaneously, which can overload the host. Optimize the models on the host beforehand with ZED Diagnostics, or headlessly with ZED_Diagnostic -c -nrlo_all, before starting any containers.
Assign a specific camera to each container
The ZED SDK opens the first available camera in the device list when no camera is specified. If several containers all start without specifying one, they race for the same camera: only one succeeds, and the others fail to open it (for example with CAMERA STREAM FAILED TO START or INVALID RESOLUTION errors) or keep retrying indefinitely.
Each container must be told which camera to open, by serial number:
- List the connected cameras and their serial numbers with
ZED_Explorer -aorZED_Studio -l. - In your own application, call
InitParameters.input.setFromSerialNumber(<serial>)before opening the camera. - With the ZED Studio tool, pass the serial number on the command line:
Some ZED tools, such as ZED Explorer or ZED Depth Viewer, don’t accept a serial number for live view (ZED Explorer’s --sn option only applies to recording or streaming). If you use one of these in a container, select the target camera manually from the dropdown at the top of its window after it starts.
Example: four cameras, four containers
This example was tested on a Jetson AGX Orin with a ZED Link Quad capture card and 4 GMSL2 cameras connected (2x ZED X Mini, 2x ZED X). Each container runs its own instance of ZED Studio, pinned to one camera, so you get 4 independent live views, each with its own Depth Map toggle to visualize that camera’s 3D depth data.
First, enable GUI containers to reach the host’s X server (see Run ZED Explorer Tool for details):
List the connected cameras to get their serial numbers:
Then start one named container per camera, all sharing the same volumes described above:
The short sleep between each docker run is deliberate: starting several GMSL2 cameras on the same link at the exact same moment can occasionally make one of them fail to start on its first attempt. This is a transient bus-arbitration issue, not a configuration problem; simply restart (docker restart <container>) any container whose camera failed to open.
The same setup works with an orchestrator (see Orchestrate Containers); the only per-service difference is the -c <serial_number> argument:
docker-compose.yaml for 4 cameras
Once the containers are running, each opens its own window with the live feed of its assigned camera. The logs confirm which serial number each container actually opened:
Enable the Depth Map eye icon next to that camera in the left panel to also visualize its live depth data.
Scaling further
Running one depth pipeline per container multiplies GPU and memory usage by the number of cameras. Before scaling up a rig, check the GPU memory and, for GMSL2 rigs, the power budget recommendations for multi-camera setups.
Setting up a single-container configuration
Getting a single container to handle several cameras correctly means opening each camera from your own application code, and persisting the host’s AI model and calibration cache so the container doesn’t redo that work on every restart.
A single-container setup runs one process that opens every camera itself, setting each one’s serial number in InitParameters before opening it (see how to list connected cameras), exactly as a custom application would outside of Docker. Since grab() blocks until a new frame is available, a common pattern for N cameras is to give each one its own thread, so every camera grabs and processes frames independently instead of waiting on the others:
Running it in Docker needs nothing beyond a single container: one docker run, with the same volumes a single camera would need for your camera family, since it’s the application, not Docker, that’s responsible for opening each camera:
Even with a single container, keep the last two volumes mounted to the host’s real /usr/local/zed/resources and /usr/local/zed/settings folders, as described above. A container’s filesystem is disposable: without these volumes, every NEURAL depth model would be re-optimized and every camera’s calibration file re-downloaded again each time the container restarts, rebuilds, or gets redeployed, adding minutes of startup time instead of reusing what’s already been optimized and downloaded on the host.

