Launch multiple docker containers at once

We all know how to launch docker containers manually via the command prompt right ? If not, maybe I should write a guide on that topic as well although I am pretty sure the internet is full of such articles.

In this post we will find out how to launch multiple docker containers at once with docker compose. If you don’t have it installed, go ahead and install it following the official documentation here. If you are using a Raspberry Pi like me and you have difficulties installing it, follow my instructions here.

Let’s go!

Configuration

In order for docker compose to launch multiple containers at once, it requires a YAML configuration file and by default it looks for docker-compose.yml. You can also use the .yaml extension but I will stick to the former.

For the purpose of this post, we will start three containers on a Raspberry Pi 3B+

  • openHAB, the open source home automation software
  • eclipse mosquitto, an open source message broker that implements MQTT
  • zigbee2MQTT, an open source Zigbee to MQTT bridge. It translates Zigbee messages to MQTT and passes them on to the broker. In our case the mosquitto server above

Here is the docker-compose.yml file but don’t run it just yet!

Hint: It is very important that you keep the indentations otherwise docker-compose will not be able to run!

version: '3.9'
services:
  openhab:
    image: openhab/openhab:3.1.0
    restart: unless-stopped
    network_mode: host
    container_name: openhab31
    devices:
      - "/dev/ttyUSB0:/dev/ttyUSB0"
    volumes:
      - "/etc/localtime:/etc/localtime:ro"
      - "/etc/timezone:/etc/timezone:ro"
      - "openhab_addons:/openhab/addons"
      - "openhab_conf:/openhab/conf"
      - "openhab_userdata:/openhab/userdata"
    environment:
      OPENHAB_HTTP_PORT: "8080"
      OPENHAB_HTTPS_PORT: "8443"
      EXTRA_JAVA_OPTS: "-Duser.timezone=Europe/Berlin"

  mqtt:
    image: eclipse-mosquitto:2.0.14
    container_name: mosquitto2014
    restart: unless-stopped
    volumes:
      - mosquitto-conf:/mosquitto/config
      - mosquitto-data:/mosquitto/data
      - mosquitto-log:/mosquitto/log
      - ./mosquitto.conf:/mosquitto/config/mosquitto.conf
    ports:
      - "1883:1883"
      - "9001:9001"

  zigbee2mqtt:
    container_name: zigbee2mqtt
    restart: unless-stopped
    image: koenkk/zigbee2mqtt
    volumes:
      - ./zigbee2mqtt-data:/app/data
      - /run/udev:/run/udev:ro
    ports:
      - 8081:8080
    environment:
      - TZ=Europe/Berlin
    devices:
      - /dev/ttyUSB0:/dev/ttyUSB0
volumes:
    mosquitto-conf:
    mosquitto-data:
    mosquitto-log:
    openhab_conf:
        driver: local
    openhab_userdata:
        driver: local
    openhab_addons:
        driver: local

Let me explain some parts of the above config that are not self explanatory

openHAB

We pull the latest stable image of openHAB at the time of writing v3.1. You can use

image: openhab/openhab:latest

to get the latest image but I prefer to have control over the version I install. If there is a newer stable image you can change the version accordingly.

We will use the network configuration of the host so essentially openHAB will have the same ip address as the Raspberry Pi

network_mode: host

Because I will be using a USB device that acts as a Zigbee gateway I will need to pass the USB information to the container so that it can access that USB port. You can find what USB port to put but unplugging / plugging back the USB key and monitoring the output of the dmesg command. In case you also have a zwave usb stick you will most likely need the following

"/dev/ttyUSB0:/dev/ttyACM0"

We also define a few named volumes in the configuration. What does this mean ? We map the folder from the host (path before the column) to the folder in the container (path after the column). That way we have access to the files located in those container folders from the host without having to access the container itself.

      - "openhab_addons:/openhab/addons"
      - "openhab_conf:/openhab/conf"
      - "openhab_userdata:/openhab/userdata"

In the environment section we configure the network ports openHAB will listen to as well as the timezone to be used.

You want to see what else you can configure ? Go here and explore!

Eclipse MQTT

We also create several volumes here in order to be able to access some files from the host without going into the container. Do you see the last line in the volumes section for the mqtt service ?

      - ./mosquitto.conf:/mosquitto/config/mosquitto.conf

We basically tell docker-compose to take the configuration file for the MQTT broker (mosquitto.conf) from the same folder we run the docker-compose command in the host. In my case I will run the command from /home/pi so I will need to create the configuration file in the same folder. Here are the contents of mosquitto.conf

listener 1883
allow_anonymous true
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

listener 1883 : the MQTT brokers listens to the default port 1883

allow_anonymous true: we don’t use authentication

persistence true: we enable persistence so that the broker stores mqtt messages it receives when the connection is not good and sends them later.

persistence_location /mosquitto/data/ : the location the broker keeps the unsent messages

log_dest file /mosquitto/log/mosquitto.log: destination and name of the log file

zigbee2MQTT

Just like the eclipse mosquitto container, we use a volume that maps a folder ( zigbee2mqtt-data) from the host to the container (/app/data). So go ahead and create the folder from the location you will run the docker compose command. In my case /home/pi

mkdir zigbee2mqtt-data

Inside the folder, create a file called configuration.yaml with the following contents

# Home Assistant integration (MQTT discovery)
homeassistant: false

# allow new devices to join
permit_join: true
frontend:
  port: 8080
# MQTT settings
mqtt:
  # MQTT base topic for zigbee2mqtt MQTT messages
  base_topic: zigbee2mqtt
  # MQTT server URL
  server: 'mqtt://mqtt'
  # MQTT server authentication, uncomment if required:
  # user: my_user
  # password: my_password

# Serial settings
serial:
  # Location of CC2531 USB sniffer
  port: /dev/ttyUSB0

Make sure that port corresponds to the correct path, in my case it is /dev/ttyUSB0. As I mentioned earlier, you can unplug and plug back in the usb Zigbee gateway and monitor the output of the dmesg command to see what’s the correct path.

When the docker container starts, it will read the local configuration file from the host since we have mapped it in the volumes section.

Pay attention also in the ports section. The format is host_port:container_port. The default configuration was 8080:8080 but I could not use it. Can you guess why ?

Port 8080 is also used by openHAB which uses the same network as the host. Remember, we have defined network_mode: host in the docker-compose file for openHAB.

So for Zigbee2MQTT I use the port 8081 in the host to avoid conflicts. All messages will arrive to the host on port 8081 which will forward them to the container on port 8080.

Launch the containers!

Finally, you can launch all the containers at once with the following command

docker-compose up -d

If everything is good you will see the following

pi@raspberrypi:~/zigbee2mqtt-data $ docker-compose up -d
[+] Running 3/0
 ⠿ Container mosquitto2014  Running                                                                                                                    
 ⠿ Container zigbee2mqtt    Running                                                                                                  
 ⠿ Container openhab31      Running      

Hint: Pay careful attention to the indentation of docker-compose.yml ! In case of doubt you can always use notepad in windows or any other indentation-friendly application. I sometimes use Visual Studio Code with indent-rainbow plugin to make sure everything is properly aligned.

Hint: You can use docker-compose logs -f to monitor the deployment of the containers

1 Comment

Leave a Comment

Your email address will not be published. Required fields are marked *

This website uses cookies. By continuing to use this site, you accept our use of cookies.