A general-purpose Docker development template for ROS2 projects. Clone this repo, configure your packages, and get a consistent, reproducible ROS2 workspace with optional Arduino firmware support.
The container is built on osrf/ros:humble-desktop-full and includes:
- ROS2 Humble with a colcon workspace (
src/) bind-mounted from the host - CycloneDDS as the ROS2 middleware
- Micro-ROS agent built into the image (
base_ws) for bridging microcontroller nodes over serial or UDP - Arduino CLI installed locally for RP2040 firmware compile and flash workflows
- Desktop shortcuts for one-click bringup and development shells
- NVIDIA GPU passthrough support (auto-detected or overridden via flags)
ros2-docker-template/
├── .env # Container name, ROS domain ID, GPU flag, firmware/run settings
├── install.sh # One-time setup and rebuild script
├── launch_container.sh # Start or attach to the container
├── run.sh # Run RUN_CMD (from .env) inside the container
├── docker-compose.yaml # Main Docker Compose file
├── docker-compose.nvidia.yaml # NVIDIA GPU overlay (merged when USE_GPU=true)
├── docker/
│ ├── Dockerfile # Image definition
│ ├── entrypoint.sh # Sources ROS2, base_ws, and shared_ws on shell startup
│ ├── bringup_entrypoint.sh # Entrypoint variant for bringup launches
│ ├── packages.txt # APT packages installed into the image
│ └── requirements.txt # Python pip packages
├── config/
│ └── cyclonedds.xml # CycloneDDS tuning (buffer sizes, multicast)
├── src/
│ └── src.repos # vcstool manifest — add your ROS2 packages here
├── data/
│ └── data.repos # vcstool manifest for large data repositories (Git LFS)
├── desktop/
│ ├── bringup.desktop # Desktop shortcut: Bringup
│ └── devel.desktop # Desktop shortcut: Development shell
├── assets/
│ ├── bringup_icon.png
│ └── devel_icon.png
└── arduino/
├── arduino-cli.yaml # Arduino CLI config (self-contained, paths relative to arduino/)
├── cores.txt # Board cores to install (one per line)
└── libraries.txt # Libraries to install (one per line)
Docker packages an application and all its dependencies into a container — a lightweight, isolated environment that runs the same way on any machine. For ROS2 development this means your workspace, system libraries, and middleware configuration are all captured in a single image, eliminating "works on my machine" problems and making it easy to share a reproducible setup with teammates or deploy to a robot.
Linux:
- Docker Engine with the Compose plugin
- NVIDIA Container Toolkit (if using GPU)
Windows (WSL2):
- Docker Desktop with the WSL 2 backend enabled
- In Docker Desktop settings, go to Resources → WSL Integration and enable integration for your WSL distro
- GPU passthrough is handled by Docker Desktop — no separate NVIDIA Container Toolkit installation is required
Clone this template and rename the folder to your project name. The container name is automatically derived from the folder name (lowercased, spaces replaced with hyphens).
Edit src/src.repos to list the repositories to clone into the workspace:
repositories:
my_package:
type: git
url: https://github.com/your-org/my_package
version: mainIf your project requires large data assets stored in Git LFS, edit data/data.repos to list the repositories to clone into data/:
repositories:
MyDataset:
type: git
url: https://github.com/your-org/MyDataset
version: maininstall.sh will clone each repository and run git lfs pull inside it. If you have no data repositories, leave data/data.repos empty.
| Variable | Default | Description |
|---|---|---|
CONTAINER_NAME |
(folder name) | Docker container/image name (set automatically by install.sh) |
ROS_DOMAIN_ID |
0 |
ROS2 domain ID |
USE_GPU |
true |
Enable NVIDIA GPU passthrough (set automatically by install.sh) |
FIRMWARE_DIR |
(empty) | Path to Arduino sketch directory (relative or absolute); leave empty to skip firmware steps |
RUN_CMD |
echo "Hello World" |
Command run by run.sh inside the container |
./install.shThe script will:
- Install
vcstoolif not present - Clone all packages from
src/src.reposintosrc/ - Install
git-lfsif not present and clone data repositories fromdata/data.repos - Auto-detect an NVIDIA GPU (override with
--gpuor--no-gpu) - Build (or rebuild) the Docker image
- Copy app files and create desktop shortcuts (
bringup.desktop,devel.desktop) on~/Desktop - Install Arduino CLI locally into
arduino/bin/if not already present - Install board cores listed in
arduino/cores.txtand libraries inarduino/libraries.txt - If
FIRMWARE_DIRis set: compile and flash the Arduino sketch to a connected RP2040 - Increase kernel socket buffer limits for DDS performance
- Build the ROS2 workspace inside the container
# Force GPU support
./install.sh --gpu
# Disable GPU support
./install.sh --no-gpuRe-running install.sh during development rebuilds the Docker image (using cache) and rebuilds the workspace.
Open a bash shell inside the container (starts the container if not already running):
./launch_container.shIf the container is already running, this attaches a new shell to it. Additional arguments are passed as a command:
./launch_container.sh colcon build
./launch_container.sh ros2 topic listrun.sh reads RUN_CMD from .env and runs it inside the container:
./run.shSet RUN_CMD to your bringup launch, test script, or any other command.
After running install.sh, two shortcuts appear on the desktop:
- Bringup — launches
RUN_CMDin a terminal - Development — opens an interactive development shell
| Path | Description |
|---|---|
/workspaces/base_ws |
Base workspace built into the image layer (contains micro_ros_setup and the Micro-ROS agent) |
/workspaces/shared_ws |
Your ROS2 packages (bind-mounted from ./src) |
/data |
Data repositories (bind-mounted from ./data) |
/config |
Runtime config including CycloneDDS (bind-mounted from ./config) |
The entrypoint sources workspaces in order: ROS2 → base_ws → shared_ws.
The image includes micro_ros_setup and a pre-built Micro-ROS agent in /workspaces/base_ws. The agent bridges microcontroller nodes (running micro-ROS firmware) into the ROS2 graph over serial or UDP.
Start the agent over serial:
source /workspaces/base_ws/install/setup.bash
ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyUSB0Start the agent over UDP:
source /workspaces/base_ws/install/setup.bash
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888The dialout group is automatically added to the container user, so serial ports are accessible without extra chmod steps.
Arduino CLI is installed locally into arduino/bin/ (not added to the system PATH). All Arduino data, downloads, and user libraries are stored under arduino/ to keep the project self-contained.
Configuring cores and libraries:
Add board cores to arduino/cores.txt (one per line):
rp2040:rp2040
Add libraries to arduino/libraries.txt (one per line):
Adafruit NeoPixel
Compiling and flashing firmware:
Set FIRMWARE_DIR in .env to the path of your Arduino sketch (the folder containing the .ino file). Re-run install.sh to compile and flash. The script will:
- Compile the sketch for the
rp2040:rp2040:adafruit_qtpyboard - Auto-detect a connected RP2040 (via serial port or RPI-RP2 bootloader drive)
- Reset the board into bootloader mode if needed and copy the
.uf2firmware
config/cyclonedds.xml is mounted into the container at /config/cyclonedds.xml and configures CycloneDDS with tuned socket buffer sizes (25 MB send/receive) for high-bandwidth topics.
Pull the latest changes for all packages in src/:
cd src
vcs pullThen rebuild the workspace:
./launch_container.sh colcon buildOr re-run install.sh for a full rebuild including the Docker image.