A Raspberry Pi is ideal for installing all kinds of programs that always have to run. The most reliable way to do that is with Docker: this way each program runs isolated in a container, so they can't interfere with each other. We'll show you how to use Docker on a Raspberry Pi and what you should pay attention to.
If you've had a Raspberry Pi for a while, chances are you'll keep installing more and more software on it. Home Assistant,Zwave2Mqtt, Node-RED, Rhasspy… That's all going well, until you update all your software to a new version, and suddenly one of your programs stops working and gives a vague error message.
What happened? A common scenario is the following. Software A and B both use version 1.0 of library C. Meanwhile, version 2.0 of library C is released, which is incompatible with C 1.0. Software A is being rewritten to use library C 2.0, while the developers of software B are not that fast and will stick with library C 1.0 for a while. You update software A and that installs library C 2.0. But Raspbian can only install one version of a library. As a result, software B suddenly no longer works, because it is not compatible with library C 2.0.
In practice, Linux distributions do everything they can to avoid this kind of situation, but it happens. Sometimes in much more subtle ways, so it's not always immediately obvious what's causing the problem.
01 What is Docker?
Docker makes it easy for developers to distribute applications so that you can run them on any Linux system. These applications can be found in the form of an image on the Docker Hub. Such an image is basically a template for a minimal Linux system, which you can run on top of Raspbian in the form of a container.
Each container is completely isolated from other containers. So the application in one container does not see the applications in other containers. And installing and updating one container ensures that the new version doesn't conflict with applications in other containers. So if you want to run more than a handful of applications on your Raspberry Pi, Docker will help you do that reliably. Thanks to Docker, you can also safely experiment with new software: you don't like it, just delete the container afterwards.
02 Install Docker
We assume that you have Raspbian installed, the Lite version will suffice. Then log in via ssh to perform the commands in this basic course. First, install Docker with the command:
curl -sSL //get.docker.com | sh
Then give the user pi (with which you are logged in) access to Docker, so you don't run all Docker commands with the command sudo must perform:
sudo usermod pi -aG docker
Log out with exit and then log in again. Now the user belongs pi to the group docker.
03 Hello world
You should now be able to boot a first Docker container:
docker run --rm hello-world
This command runs the Docker container hello-world. This container shows in its output what exactly happens: the image is not found on your Raspberry Pi and is then downloaded by Docker from the Docker Hub. Then Docker creates a container based on this image and runs the program in it. By the option --rm the container is cleaned up after the program is closed. You now know that Docker is correctly installed and working.
Hypriot
We'll simply install Docker on Raspbian in this basic tutorial, but other operating systems are also possible if you're interested in Docker on a Raspberry Pi. For example, there is Hypriot: an operating system for the Raspberry Pi that is optimized for using Docker. You then only have to install this image on the micro-SD card of your Raspberry Pi and you can immediately start using Docker. Hypriot is especially interesting if you're just running Docker containers on your Raspberry Pi and nothing else.
04 Create containers
The basics of working with Docker containers is done with the command docker, as we showed in the previous step. Usually with Docker you don't want to run a container and immediately shut it down, but let it run. So we don't use the option --rm. Plus, you want that container to run in the background, without seeing the output on the screen all the time. The option -d.
If you started a container this way, Docker would give it an arbitrary name, which is not useful if you have more than a handful of containers. With the option --name NAME you therefore give the container a fixed name.
Then you also have to look at the network connections. Since each Docker container is isolated, you can't just access for example a web server running on port 80 in a container. Therefore, you need to instruct Docker to forward any request on, say, port 8888 on the Raspberry Pi to port 80 in a specific container. You do that with the option -p 8888:80. Putting all these options together for the sample container containous/whoami, run the following command:
docker run -d --name whoami -p 8888:80 containous/whoami
If all goes well, after a while you will see a long string of hexadecimal numbers (like 5122c935ce5178751a59699d2c5605c607700bd04e5f57a6c18de434ae53956e). This is the ID of the container. If you now surf in your web browser to //IP:8888 with instead of IP the IP address of your Raspberry Pi, you will see a web page that is generated by the web server in the container.
05 View your containers
Once you have started up a few containers like this, management starts to become important. First of all, it is useful to see which containers are running:
docker ps
You will then see information about all containers that are active (with the option -a including the containers that have stopped). The first column contains a unique ID for each container, next to it the image from which the container was created. The column STATUS best if you have problems. For example, if your container keeps restarting due to a problem, you'll see it here.
In the column PORTS you see the ports used. For example, in front of our container whoami is 0.0.0.0:8888->80/tcp. That means tcp port 8888 on the Raspberry Pi will be redirected to tcp port 80 on the container. In the last column you see the name of the container, which you can use in further Docker commands.
If you want more information, the command will come docker stats useful. You will then see statistics for each container, such as the consumption of the processor, memory and network. If you want all the information Docker knows about a specific container, run this command with the container's ID or name:
docker inspect CONTAINER
And finally, if you want to view the logs of a container, run one of these two commands:
docker logs CONTAINER
docker logs -f CONTAINER
With the option -f follow the logs in real time as the container generates them.
06 Manage your containers and images
If you want to stop, start or restart a container that is running, you can do so easily with these commands respectively:
docker stop CONTAINER
docker launch CONTAINER
docker restart CONTAINER
If you want to temporarily pause a container (all programs in it will be temporarily 'frozen'), run this command:
docker pause CONTAINER
After this command, all programs in the container will run again:
docker unpause CONTAINER
With the command docker images you will see the list of images that Docker has downloaded. For our whoami container see in the column REPOSITORY the text containous/whoami standing and in the column TAG stands latest. The full name of the image would be containous/whoami:latest are, but those latest is the default value for the tag, so can be omitted. That is why, in our assignment in section 4, we docker run just containous/whoami as an image.
In the column CREATED see how long ago this image was downloaded. To update this image, run the following command:
docker pull containous/whoami:latest
Docker then downloads the latest version of the image or tells you that the image is up to date. If you do it again docker images you will see that an image has been added.
But the current whoami container still uses the old image. To upgrade this, stop (docker stop whoami) and delete (docker rm wohami) you create the container, and recreate the container using the docker run command from section 4.
To clean up
If you regularly update your Docker images to run the latest version in a container, the old images will remain. The micro-SD card of your Raspberry Pi with a capacity of at most a few tens of gigabytes can quickly fill up, especially if you run large containers. For example, containers such as those from Home Assistant and Rhasspy are more than a gigabyte in size. Now Docker works with a system so that with an update not that full gigabyte is downloaded and stored again, but after many updates the required storage continues to increase. With the command docker rmi IMAGE_ID delete an image based on the ID you specify in the output of the command docker images finds. Docker also knows the command docker image prune which removes all images that are not used by a container. Of docker system prune also remove stopped containers, networks not used by at least one container, and cache files.
07 Volume
Our example container whoami did not use any configuration data or data. But you can share a directory on your Raspberry Pi with a Docker container so that it can access data from it. Docker calls such a shared directory a volume.
If you are going to work with multiple containers on your Raspberry Pi, it is recommended that you put their directories all together. Create a directory for that, for example with:
mkdir -p /home/pi/containers/nginx/data
Then place in the folder containers/nginx/data a file index.html with an html page.
Then you can now start a container with nginx (a web server) with which you share this directory:
docker run -d --name nginx -p 8080:80 -v /home/pi/containers/nginx/data:/usr/share/nginx/html:ro nginx
Then the container starts up with the web server and mounts the directory /home/pi/containers/nging/data on your Raspberry Pi in the container at the location /usr/share/nginx/html, with read only permissions (ro stands for read only). If you surf now to IP:8080 do you get the html file index.html to see.
08 Docker Compose
So far we have manually started Docker containers with command docker run. But if you're running a few more Docker containers and want to change their configuration regularly, a different approach is better: putting everything in one configuration file. That goes with Docker Compose.
To do that, first install Python's package manager pip and then Docker Compose (which is a Python program) with these commands:
sudo apt install python3-pip
sudo pip3 install docker-compose
Now you can configure multiple Docker containers in one file docker-compose.yml to make. To do this, create a Docker Compose file with:
nano docker-compose.yml
Put in there the following configuration for our example containers whoami and nginx:
version: '3.7'
services:
whoami:
image: containous/whoami
container_name: whoami
restart: always
port:
- 8888:80
nginx:
image: nginx
container_name: nginx
restart: always
port:
- 8080:80
volume:
- /home/pi/containers/nginx/data:/usr/share/nginx/html:ro
09 YAML
Save the file with Ctrl+O and exit nano with Ctrl+X. This is a YAML file (with the extension .yml). YAML (stands for the recursive abbreviation "YAML Ain't Markup Language") is a file format for defining configuration data in a readable way. More info can be found on the official website.
You can see in this file that we define two containers as services. For each container we define the image used, the name that the container should be given and whether the container should restart automatically in case of problems. In addition, we also define the redirected ports and the volumes.
You can also find all this information on the command lines with docker run, but in this Docker Compose file it's a bit more organized.
10 Working with Docker Compose
Once you have a file docker-compose.yml you can easily create and run the containers defined in it:
docker-compose up -d
After that you can manage these containers with the docker command, but docker-compose itself also has a lot of options specifically to manage containers that you have created with Docker Compose. This is how you clean everything up with the following command, all defined containers will be stopped and deleted:
docker compose down
You can also follow the logs of all containers with:
docker-compose logs -f
Each container displays its log messages in a different color. Docker Compose also has a familiar tune for stopping, starting, and restarting all containers:
docker compose stop
docker compose start
docker compose restart
Update all containers in your Docker Compose file with the following two commands:
docker compose pull
docker compose restart
The first command will download new images for all the containers you defined and the second command will reboot all those containers so that they use the new image. After that you can delete the old images if desired with:
docker image prune
11 And beyond
You can find Docker images of many applications on Docker Hub. On LinuxServer.io you will also find dozens of Docker images maintained by volunteers. These images are well maintained and documented, and they all use a similar approach and basic infrastructure.
Try to limit yourself to 'official' Docker images, which are provided by a project itself, or images from reliable parties such as LinuxServer.io. Because in principle anyone can publish Docker images on Docker Hub, but they are not always kept up to date.
The right processor architecture
It is important that you download Docker images for the correct processor architecture. The Raspberry Pi has an ARM processor, which is not compatible with the Intel or AMD processors found in PCs. Many Docker images are published to automatically download the correct version for your processor architecture. On Docker Hub, you will find which architectures are supported under the desired Docker image page. For Raspbian that is arm32v7, arm/v7 or armhf. If you get the error message when starting a Docker container: exec format error you probably downloaded an image of the wrong processor architecture. If that happens, you will need to download an image with a different tag. For example, the motionEye project distributes its official Docker image with two possible tags: you run ccrisan/motioneye:master-amd64 on Intel-compatible processors and ccrisan/motioneye:master-armhf on a Raspberry Pi.