Installing Docker
Install docker on Debian bookworm following the instructions at https://docs.docker.com/engine/install/debian/
I used the method in the section: Install using the apt repository
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Next follow the post install steps:
https://docs.docker.com/engine/install/linux-postinstall/
Create the docker group.
sudo groupadd docker
Add your user to the docker group.
sudo usermod -aG docker $USER
Log out and log back in so that your group membership is re-evaluated.
Now you should be able to run docker commands without needing to use sudo.
Test docker is working:
docker run hello-world
This should show a success message.
Building a ROS Docker Container
Following the documentation at https://docs.ros.org/en/jazzy/How-To-Guides/Installing-on-Raspberry-Pi.html
I built a docker image myself, in order to get the desktop variant required to run the tutorials. First clone the ROS docker images git repo from your home directory:
git clone https://github.com/osrf/docker_images
The docs then say to navigate to the folder for the release you want and build the container. But which folder to choose? I went with the ‘Jazzy Jalisco’ version of ROS2, as the latest stable supported release so it has the longest support lifetime. You also need to choose a version of Ubuntu to run it on. I chose noble as the only option for Jazzy docker files for all the ROS distributions. We are going to build the desktop version, as this has all the tools we need for the tutorials. But the container image will be around 2.4GB in size, so make sure you have enough free disk space. The ROS tutorials should work with other supported distributions, so you could choose the rolling/jammy option if you want to try the latest development build. But while I found this worked for the ROS CLI tools tutorials, it failed to build all the examples in the Client libraries tutorial. Also the Rolling desktop docker image was 3.3GB in size, and used python 3.10 whereas the jazzy image used python 3.12. So I have switched back to Jazzy for now.
Navigate to the folder ~/docker_images/ros/jazzy/ubuntu/noble/desktop
Then run the command:
docker build -t ros_docker .
Note the ‘.’ on the end of the command, it is important, as it tells docker to use the docker file in the folder you are running the command in.
This creates a docker container image, not a container. It will take a while (a go and make a coffee amount of time). When it completes, you should be able to see a docker image named ros_docker if you run the command:
docker image ls
Now we can create a docker container from our image:
docker run -it ros_docker
This will create a contain from the image and open an interactive shell in the container, so now the command prompt is open in a shell inside the container.
At this point we have a ROS container. Open a new terminal window on the Pi and type:
docker ps
You should see your container listed as a running container.
We need to configure the environment for ROS. https://docs.ros.org/en/jazzy/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.html
We want this configuration to apply for any future shell sessions so we will put the commands into the bash.rc file in our container by entering the command:
echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc
Now if we exit and re-enter our container the environment configuration should stick. Type ‘exit’ to leave the container shell. The container remains running (check with docker ps). We can enter a new interactive shell in it by using the container name. Names are generated randomly when containers are created. So you need to note the name your container has when you list it with the docker ps command. Then run:
docker exec -it <container_name> bash
But substitute <container_name> with the actual name of your container. Once in the new shell, we can test that our environment is configured as expected:
printenv | grep -i ROS
This should list all the ROS environment variables. So you should see something like this:
ROS_VERSION=2
ROS_PYTHON_VERSION=3
PWD=/ros2_ws
AMENT_PREFIX_PATH=/opt/ros/jazzy
CMAKE_PREFIX_PATH=/opt/ros/jazzy/opt/gz_math_vendor:/opt/ros/jazzy/opt/gz_utils_vendor:/opt/ros/jazzy/opt/gz_cmake_vendor
ROS_AUTOMATIC_DISCOVERY_RANGE=SUBNET
PYTHONPATH=/opt/ros/jazzy/lib/python3.12/site-packages
LD_LIBRARY_PATH=/opt/ros/jazzy/opt/rviz_ogre_vendor/lib:/opt/ros/jazzy/lib/aarch64-linux-gnu:/opt/ros/jazzy/opt/gz_math_vendor/lib:/opt/ros/jazzy/opt/gz_utils_vendor/lib:/opt/ros/jazzy/opt/gz_cmake_vendor/lib:/opt/ros/jazzy/lib
PATH=/opt/ros/jazzy/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ROS_DISTRO=jazzy
If all that is as expected, then we should be able to run a ros2 command:
ros2 pkg executables
This should list all the ros2 packages which are installed (the desktop version has quite a lot of them).
We now have a working ROS2 install on our Raspberry Pi. But before we get into the tutorials, it will be helpful to know a few more docker commands.
Some Useful Docker Commands
Now we have played around with docker a bit, it is useful to know how to manage the docker artifacts we may have created. For example, we will have created a local copy of the hello-world image, and a hello-world container for each time we ran the docker run hello-world command.
List your docker images:
docker image ls
List your docker containers:
docker ps -a
(The -a flag includes stopped containers. Without it, only running containers are shown).
To stop and start containers, we can use their container names with these commands:
docker start <container_name>
docker stop <container_name>
To clean up docker containers, note their container ID shown with the docker ps -a command and delete them using their ID:
docker rm <container ID>
Each time we use the docker run command with an image name, we create a new container. We don’t want this, as our environment will not be configured in the new container. So use start and stop instead once we have a container. (This goes against the philosophy of Docker, where containers are created and destroyed so you always run from a clean state. But we are just running some tutorials here, so our container will be treated as persistent with the configuration here. We’ll go deeper into docker and the docker compose tools in another tutorial).
We have run some shell commands, but our container will not have a graphical context so the GUI tools will not run. Try running the rqt command in your container and you will get an error about not being able to access the display. We need to give our container the hooks to access our display.
Following: https://wiki.ros.org/docker/Tutorials/GUI
I used the simple (insecure) option. Unfortunately you have to provide the parameters when you create the container, so we will have to start again with a fresh container.
docker run -it \
--env="DISPLAY" \
--env="QT_X11_NO_MITSHM=1" \
--volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
ros_docker
Now we have a container which can access the graphics display of the host Pi. We need to configure the environment again in our new container:
echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc
This only needs to be done once in the new container. We also need to grant permissions to allow the display to be accessed. In a terminal in the host Pi, run the command:
xhost +local:root
Note this is not secure, but it only lasts until the next reboot, so I was happy with this on a private network. The linked article goes into more detail, and gives some other options. But this was the simplest one to get working on a Raspberry Pi.
Now, in the shell in the container, you should be able to run the command:
rqt
The GUI tool window should open successfully on your host Pi desktop. Now you have a container which can run graphical tools. If you get an authorisation error, check you have run the xhost +local:root command again since you last booted up the Pi.
Remember we just created a new container, so run docker ps -a to find out what the new container name is, and use this one for the graphical tools in the tutorials. You can rm the older container as we no longer need it.
Recap
First we built a docker container image for a specific version of Ubuntu and a specific ROS2 distribution. We used the desktop variant as it contains all the packages needed for graphical demos and the tutorials.
Next we created a docker container using this image to run ROS2. We created it with a configuration so it can run graphical tools on the host desktop. We set up the bash.rc file to configure the shell each time we start a new shell, so the ROS2 tools can be run. Once we had created this container, we start it with docker start <container_name> and enter a new shell with the command docker exec -it <container_name> bash
We don’t need to use docker run again, unless we want to create a new container with a different configuration.
Running the TurtleSim Tutorial in Docker
The turtlesim tutorial takes you through some basics of ROS using a graphical simulation tool and the ROS GUI tool RQT. The documentation is at:
https://docs.ros.org/en/jazzy/Tutorials/Beginner-CLI-Tools/Introducing-Turtlesim/Introducing-Turtlesim.html
But we can skip the install steps with our preconfigured docker container and jump straight to the run step. First open an interactive shell in your docker container (you need it to be started first). Then in the shell, run the command:
ros2 run turtlesim turtlesim_node
The turtlesim window should appear on your host desktop.
The shell window will be tied up now, running the simulation node and displaying output from it. So to run another node to control the turtle, we need to open a new terminal window on the host, and start a second interactive shell in the same docker container:
docker exec -it <container_name> bash
Now in this second shell, we can run the turtle_teleop_key node:
ros2 run turtlesim turtle_teleop_key
This should launch the turtle_teleop_key node and you should be able to control the turtle with the arrow keys. The tutorial then suggests some ros2 commands you can run. So you will need a new terminal window to execute another interactive shell in the docker container. You can use this to run the ros2 commands to list nodes, and later run the rqt tool to follow the tutorial steps to execute various api calls to the turtlesim node.
Hopefully this has got you up and running with ROS in Docker on the raspberry Pi. If there are any steps that didn’t make sense, please let me know so I can fill in any gaps in this guide.
See the next post in this blog for how to extend this using Docker Compose.