Docker

It is possible to run Zigbee2MQTT in a Docker container using the official Zigbee2MQTT Docker imageopen in new window.

This image support the following architectures: 386, amd64, arm/v6, arm/v7, arm64. Since Zigbee2MQTT images are manifest listed, Docker will auto-detect the architecture and pull the right image.

Note for Raspberry Pi 1 and zero users: there is a bug in Docker which selects the wrong image architecture. Before executing docker run pull the correct image with docker pull koenkk/zigbee2mqtt --platform linux/arm/v6.

Start by figuring out the location of your adapter as explained here.

Creating the initial configuration

Navigate to the directory where you whish to store the Zigbee2MQTT data and execute:

wget https://raw.githubusercontent.com/Koenkk/zigbee2mqtt/master/data/configuration.yaml -P data
1

Now configure the MQTT server and adapter location as explained here.

Running the container

Execute the following command, update the --device parameter to match the location of your adapter.

$ docker run \
   --device=/dev/ttyACM0 \
   -v $(pwd)/data:/app/data \
   -v /run/udev:/run/udev:ro \
   -e TZ=Europe/Amsterdam \
   koenkk/zigbee2mqtt
1
2
3
4
5
6

Parameters explanation:

  • --device=/dev/ttyACM0: Location of adapter (e.g. CC2531)
  • -v $(pwd)/data:/app/data: Directory where Zigbee2MQTT stores it configuration (pwd maps to the current working directory)
  • -v /run/udev:/run/udev:ro: only required for auto-detecting the port and some adapters like ConBee
  • -e TZ=Europe/Amsterdam: Configure the timezone

TIP

If you run the MQTT-Server on the same host (localhost) you could use the IP of the docker0 bridge to establish the connection: server: mqtt://172.17.0.1.

Rootless container

To improve the security of the deployment you may want to run Zigbee2MQTT as a non-root user.

  1. Identify the group that has access to the adapter (in Ubuntu, e.g. it might be assigned to dialout). Update ttyACM0 to match your adapter location.
$ ls -l /dev/ttyACM0
crw-rw---- 1 root dialout 166, 0 Nov 5 16:31 /dev/ttyACM0
1
2
  1. If you want to run Zigbee2MQTT using your current user find the uid (UserID) and gid (GroupID):
$ id
uid=1001(pi) gid=1001(pi) Groups=...
1
2
  1. Start the docker container after updating device, user (uid:gid) and group-add:
$ sudo docker run \
   --name=zigbee2mqtt \
   -v $(pwd)/data:/app/data \
   -v /run/udev:/run/udev:ro \
   --device=/dev/ttyACM0 \
   --user 1001:1001 \
   --group-add dialout \
   -e TZ=Europe/Amsterdam \
   koenkk/zigbee2mqtt
1
2
3
4
5
6
7
8
9

Parameters explanation:

  • --user 1001:1001: Run the Zigbee2MQTT process within the container using the provided UserID and GroupID
  • --group-add dialout: Assign the dialout group to the user to be able to access the device

Updating

To update to the latest Docker image:

docker pull koenkk/zigbee2mqtt:latest
docker rm -f zigbee2mqtt
# Now run the container again with the instructions above
1
2
3

Tags

The following tags are available:

  • Latest release version: latest
  • Latest dev version (based on devopen in new window branch): latest-dev
  • Specific release version, e.g: 1.7.0

Docker Compose

Example of a Docker Compose file:

version: '3.8'
services:
  zigbee2mqtt:
    container_name: zigbee2mqtt
    image: koenkk/zigbee2mqtt
    restart: unless-stopped
    volumes:
      - ./data:/app/data
      - /run/udev:/run/udev:ro
    ports:
      # Frontend port
      - 8080:8080
    environment:
      - TZ=Europe/Berlin
    devices:
      # Make sure this matched your adapter location
      - /dev/ttyUSB0:/dev/ttyACM0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

You can also run a rootless container with docker-compose by adding the required attributes to the zigbee2mqtt service block in your docker-compose-yml:

    group_add:
      - dialout
    user: 1000:1000
1
2
3

Starting the container

To start the Docker container:

docker-compose up -d zigbee2mqtt
1

You can optionally skip zigbee2mqtt and it will start all containers listed in the compose file.

Updating

To update to the latest Docker image:

docker-compose pull zigbee2mqtt
docker-compose up -d zigbee2mqtt
1
2

You can optionally skip zigbee2mqtt and it will pull any new images for all containers in the compose file, and then restart those that were updated.

Docker Stack device mapping

This is only relevant when using Docker Stack

Docker stack doesn't support device mappings with option --devices when deploying a stack in Swam mode. A workaround is to bind the device as volume binding and set the right permissions.

The workaround is based on the solution found at Add support for devices with "service create"open in new window, all credits goes this him.

  1. Identify serial adapter Identify the serial adapter using the following command:

    sudo lsusb -v
    
    1
    Bus 001 Device 005: ID 0451:16a8 Texas Instruments, Inc.
    Device Descriptor:
      bLength                18
      bDescriptorType         1
      bcdUSB               2.00
      bDeviceClass            2 Communications
      bDeviceSubClass         0
      bDeviceProtocol         0
      bMaxPacketSize0        32
      idVendor           0x0451 Texas Instruments, Inc.
      idProduct          0x16a8
      bcdDevice            0.09
      iManufacturer           1 Texas Instruments
      iProduct                2 TI CC2531 USB CDC
      iSerial                 3 __0X00124B001936AC60
      bNumConfigurations      1
      Configuration Descriptor:
    	bLength                 9
    	bDescriptorType         2
    	wTotalLength           67
    	bNumInterfaces          2
    	bConfigurationValue     1
    	iConfiguration          0
    	bmAttributes         0x80
    	  (Bus Powered)
    	MaxPower               50mA
    	Interface Descriptor:
    	  bLength                 9
    	  bDescriptorType         4
    	  bInterfaceNumber        0
    	  bAlternateSetting       0
    	  bNumEndpoints           1
    	  bInterfaceClass         2 Communications
    	  bInterfaceSubClass      2 Abstract (modem)
    	  bInterfaceProtocol      1 AT-commands (v.25ter)
    	  iInterface              0
    	  CDC Header:
    		bcdCDC               1.10
    	  CDC ACM:
    		bmCapabilities       0x02
    		  line coding and serial state
    	  CDC Union:
    		bMasterInterface        0
    		bSlaveInterface         1
    	  CDC Call Management:
    		bmCapabilities       0x00
    		bDataInterface          1
    	  Endpoint Descriptor:
    		bLength                 7
    		bDescriptorType         5
    		bEndpointAddress     0x82  EP 2 IN
    		bmAttributes            3
    		  Transfer Type            Interrupt
    		  Synch Type               None
    		  Usage Type               Data
    		wMaxPacketSize     0x0040  1x 64 bytes
    		bInterval              64
    	Interface Descriptor:
    	  bLength                 9
    	  bDescriptorType         4
    	  bInterfaceNumber        1
    	  bAlternateSetting       0
    	  bNumEndpoints           2
    	  bInterfaceClass        10 CDC Data
    	  bInterfaceSubClass      0 Unused
    	  bInterfaceProtocol      0
    	  iInterface              0
    	  Endpoint Descriptor:
    		bLength                 7
    		bDescriptorType         5
    		bEndpointAddress     0x84  EP 4 IN
    		bmAttributes            2
    		  Transfer Type            Bulk
    		  Synch Type               None
    		  Usage Type               Data
    		wMaxPacketSize     0x0040  1x 64 bytes
    		bInterval               0
    	  Endpoint Descriptor:
    		bLength                 7
    		bDescriptorType         5
    		bEndpointAddress     0x04  EP 4 OUT
    		bmAttributes            2
    		  Transfer Type            Bulk
    		  Synch Type               None
    		  Usage Type               Data
    		wMaxPacketSize     0x0040  1x 64 bytes
    		bInterval               0
    Device Status:     0x0000
      (Bus Powered)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
  2. UDEV Rules

    Create a new udev rule for serial adpater, idVendor and idProduct must be equal to values from lsusb command. The rule below creates device /dev/zigbee-serial:

    echo "SUBSYSTEM==\"tty\", ATTRS{idVendor}==\"0451\", ATTRS{idProduct}==\"16a8\", SYMLINK+=\"zigbee-serial\",  RUN+=\"/usr/local/bin/docker-setup-zigbee-serial.sh\"" | sudo tee /etc/udev/rules.d/99-zigbee-serial.rules
    
    1

    Reload newly created rule using the following command:

    sudo udevadm control --reload-rules
    
    1
  3. Create docker-setup-zigbee-serial.sh

    sudo nano /usr/local/bin/docker-setup-zigbee-serial.sh
    
    1

    Copy the following content:

    #!/bin/bash
    USBDEV=`readlink -f /dev/zigbee-serial`
    read minor major < <(stat -c '%T %t' $USBDEV)
    if [[ -z $minor || -z $major ]]; then
    	echo 'Device not found'
    	exit
    fi
    dminor=$((0x${minor}))
    dmajor=$((0x${major}))
    CID=`docker ps -a --no-trunc | grep koenkk/zigbee2mqtt | head -1 |  awk '{print $1}'`
    if [[ -z $CID ]]; then
    	echo 'CID not found'
    	exit
    fi
    echo 'Setting permissions'
    echo "c $dmajor:$dminor rwm" > /sys/fs/cgroup/devices/docker/$CID/devices.allow
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    Set permissions:

    sudo chmod 744 /usr/local/bin/docker-setup-zigbee-serial.sh
    
    1
  4. Create docker-event-listener.sh

    sudo nano /usr/local/bin/docker-event-listener.sh
    
    1

    Copy the following content:

    #!/bin/bash
    docker events --filter 'event=start'| \
    while read line; do
    	/usr/local/bin/docker-setup-zigbee-serial.sh
    done
    
    1
    2
    3
    4
    5

    Set permissions:

    sudo chmod 744 /usr/local/bin/docker-event-listener.sh
    
    1
  5. Create docker-event-listener.service

    sudo nano /etc/systemd/system/docker-event-listener.service
    
    1

    Copy the following content:

    [Unit]
    Description=Docker Event Listener for Zigbee serial adapter
    After=network.target
    StartLimitIntervalSec=0
    [Service]
    Type=simple
    Restart=always
    RestartSec=1
    User=root
    ExecStart=/bin/bash /usr/local/bin/docker-event-listener.sh
    
    [Install]
    WantedBy=multi-user.target
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    Set permissions:

    sudo chmod 744 /etc/systemd/system/docker-event-listener.service
    
    1

    Reload daemon

    sudo systemctl daemon-reload
    
    1

    Start Docker event listener

    sudo systemctl start docker-event-listener.service
    
    1

    Status Docker event listener

    sudo systemctl status docker-event-listener.service
    
    1

    Enable Docker event listener

    sudo systemctl enable docker-event-listener.service
    
    1
  6. Verify and deploy Zigbee2MQTT stack

    Now reconnect the serial adapter. Verify using the following command:

    ls -al /dev/zigbee-serial
    
    1
    lrwxrwxrwx 1 root root 7 Sep 28 21:14 /dev/zigbee-serial -> ttyACM0
    
    1

    Below an example of a docker-stack-zigbee2mqtt.yml:

    version: "3.7"
    services:
      zigbee2mqtt:
    	image: koenkk/zigbee2mqtt:latest-dev
    	environment:
    	  - TZ=Europe/Amsterdam
    	volumes:
    	  - /mnt/docker-cluster/zigbee2mqtt/data:/app/data
    	  - /dev/zigbee-serial:/dev/zigbee-serial
    	networks:
    	  - proxy_traefik-net
    	deploy:
    	  placement:
    		constraints: [node.hostname == rpi-3]
    	  replicas: 1
    
    networks:
      proxy_traefik-net:
    	external: true
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    In the above example, proxy_traefik-net is the network to connect to the mqtt broker. The constraint makes sure Docker deploys only to this (rpi-3) node, where the serial adapter is connected to. The volume binding /mnt/docker-cluster/zigbee2mqtt/data is the zigbee2mqtt persistent directory, where configuration.yaml is saved.

    The zigbee2Zigbee2MQTTmqtt configuration.yaml should point to /dev/zigbee-serial:

    [...]
    serial:
      port: /dev/zigbee-serial
    [...]
    
    1
    2
    3
    4

    Deploy the stack:

    docker stack deploy zigbee2mqtt --compose-file docker-stack-zigbee2mqtt.yml
    
    1

Docker on Synology DSM 7.0

NOTE: This may not work with all Zigbee controllers, but has been tested with the CC2531.

As of Disk Station Manager version 7, Synology removed the built-in support for USB-devices like a Zigbee controller. The USB support can be installed to the Linux kernel by issuing the following commands as root.

modprobe usbserial
modprobe ftdi_sio
modprobe cdc-acm
1
2
3

After issuing the commands, the Zigbee controller may need to be unplugged and re-inserted to the USB port.

You may also need additional drivers based on your USB Zigbee controlller setup, e.g. CH341 module is not included by default. You can download precompiled modules from jadahl.com pages - select module directory based on NAS CPU architecture (DS218+ -> INTEL Celeron J3355 -> Apollo Lake).

cd /lib/modules
wget http://www.jadahl.com/iperf-arp-scan/DSM_7.0/apollolake/ch341.ko
insmod /lib/modules/ch341.ko
1
2
3

It is possible to create a start-up task that issues the above commands:

  1. Create an executable script file that contains the three modprobe commands.
  2. Using DSM's Control Panel -> Task Scheduler -> Create -> Triggered Task -> User-defined script with the settings: User: root, Event: Boot-up, and a bash command executing the executable file under Task Settings.