Example Config: Docker with Home Assitant, Mosquitto, Zigbee2Mqtt and Traefik

This is how I have set up Home Assistant in a Docker, running on a Raspberry Pi, with Mosquitto, Zigbee2Mqtt and Traefik as reverse proxy.

raspi06.drawio

I initially had all containers in the “proxy” Docker network, but that caused problems with Sonos, Chrome Cast. etc. so I put the Home Assistant container in network-mode host to fix the access problems.
This also allows accessing Home Assistant via HTTP, which is necessary to e.g. get text-to-speech i.e. streaming for media to work.
Traefik exposes HTTP only to redirect it to HTTPS.
In general I use a Let’s Encrypt wildcard certificate for my local network, but I had to create a special one for MQTTS, because Tasmota has some restrictions. The certificates are “deployed” via SMB shares to various Raspberry Pis.
All Docker containers are controlled via a single compose file.
Watchtower is used to notify about new docker versions.

compose.yml:

# the name is required, because when it is not set docker uses the directory name as default
# and since this compose file is linked to multiple directories that woulkd be a problem
name: home_assistant

services:

  homeassistant:
    image: homeassistant/home-assistant:2023.10.3
    container_name: "homeassistant"
    restart: always
    stop_grace_period: 2m
    volumes:
      - "/opt/homeAssistant/cfg:/config"
      - "/opt/letsencrypt:/letsencrypt:ro"
    network_mode: "host"
    # HASS was originally in the proxy network and could hence resolve mosquitto via its ervice name
    # Since HASS is now in network mode "host" that resolution no longer works.
    # To not have to change the configs inside HASS static IPs are assigned to the docker services
    # and "extra_hosts" provides DNS name "mosquitto" to HASS.
    extra_hosts:
      - "mosquitto:172.21.0.2"
    devices:
      - "/dev/ttyUSB_RfxTrx:/dev/ttyUSB_RfxTrx"
    environment:
      - TZ=Europe/Vienna
    labels:
    - "com.centurylinklabs.watchtower.enable=true"
    depends_on:
      - mosquitto


  zigbee2mqtt:
    image: koenkk/zigbee2mqtt:1.33.0
    container_name: zigbee2mqtt
    restart: always
    stop_grace_period: 2m
    networks:
      proxy:
        # The static IP is not really necessary, but adedd for symmetry
        ipv4_address: 172.21.0.4
    volumes:
      - /opt/zigbee2mqtt/data:/app/data
      - /run/udev:/run/udev:ro
    devices:
      - /dev/ttyUSB_SonoffZigbee:/dev/ttyUSB0
    environment:
      TZ: Europe/Vienna
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
    depends_on:
      - mosquitto


  traefik:
    image: traefik:2.10.4
    container_name: traefik
    command: --configFile=/etc/traefik/traefik.yml
    restart: always
    stop_grace_period: 2m
    networks:
      proxy:
        # The static IP is not really necessary, but adedd for symmetry
        ipv4_address: 172.21.0.3
    ports:
      - 80:80
      - 443:443
      - 8883:8883
    volumes:
      - /opt/traefik/cfg:/etc/traefik
      - /opt/traefik/log:/var/log
      - /opt/letsencrypt:/letsencrypt:ro
    labels:
      - "com.centurylinklabs.watchtower.enable=true"


  mosquitto:
    image: "eclipse-mosquitto:2.0.17"
    container_name: "mosquitto"
    restart: always
    stop_grace_period: 2m
    volumes:
      - "/opt/mosquitto/cfg:/mosquitto/config"
      - "/opt/mosquitto/data:/mosquitto/data"
      - "/opt/mosquitto/log:/mosquitto/log"
      - "/opt/letsencrypt:/letsencrypt:ro"
    networks:
      proxy:
        # The static IP is needed for HASS - see comments in service "homeassistant" above.
        ipv4_address: 172.21.0.2
    environment:
      TZ: Europe/Vienna
    labels:
      - "com.centurylinklabs.watchtower.enable=true"

# A network definition is necessary because homeassistant needs to define the IP or IP range
# of the reverse proxy in the configuration of the http integration.
# It is also necessary for the reverse proxy and to assign static IPs.
networks:
  proxy:
    ipam:
      config:
        - subnet: 172.21.0.0/16

traefik.yml:

global:
  checkNewVersion: true
  sendAnonymousUsage: true

entryPoints:
  http:
    address: ":80"
  https:
    address: ":443"
  mqtt:
    address: ":1883"
  mqtts:
    address: ":8883"


providers:
  file:
    # dynamic config reload does not work - docker?
    filename: "/etc/traefik/dynamic.yml"
    watch: true

api:
  dashboard: true


# See https://doc.traefik.io/traefik/v2.10/observability/logs/
log:
  filePath: "/var/log/traefik.log"
#  level: "DEBUG"
#  level: "INFO"
  level: "WARN"

This is the dynamic Traefik config file:
dynamic.yaml:

tcp:
  routers:

    mosquitto-MQTT:
      service: "mosquitto"
      rule: "HostSNI(`*`)"
      entryPoints: mqtts
      tls: {}

  services:

    mosquitto:
      loadBalancer:
        servers:
          - address: "mosquitto:1883"



http:

  routers:

    homeassistant-HTTP:
      service: "homeassistant"
      rule: "Host(`homeassistant.mydomain.com`)"
      entryPoints: http
      middlewares:
        - "http2https"
    homeassistant-HTTPS:
      service: "homeassistant"
      rule: "Host(`homeassistant.mydomain.com`)"
      entryPoints: https
      tls: {}

    traefik-HTTP:
      service: "api@internal"
      rule: "Host(`traefik-raspi06.mydomain.com`)"
      entryPoints: http
      middlewares:
        - "http2https"
    traefik-HTTPS:
      service: "api@internal"
      rule: "Host(`traefik-raspi06.mydomain.com`)"
      entryPoints: https
      tls: {}
      middlewares:
        - traefikAuth

    zigbee2mqtt-HTTP:
      service: "zigbee2mqtt"
      rule: "Host(`zigbee2mqtt.mydomain.com`)"
      entryPoints: http
      middlewares:
        - "http2https"
    zigbee2mqtt-HTTPS:
      service: "zigbee2mqtt"
      rule: "Host(`zigbee2mqtt.mydomain.com`)"
      entryPoints: https
      tls: {}
      middlewares:
        - zigbee2mqttAuth


  middlewares:
    http2https:
      redirectScheme:
        scheme: https
        permanent: true

# See https://doc.traefik.io/traefik/v2.10/middlewares/http/basicauth/
    traefikAuth:
      basicAuth:
        usersFile: "/etc/traefik/passwd.traefik"
    zigbee2mqttAuth:
      basicAuth:
        usersFile: "/etc/traefik/passwd.zigbee2mqtt"

  services:

    homeassistant:
      loadBalancer:
        servers:
# Use IP instead of homeassistant.mydomain.com to be resilient against DNS problems
          - url: "http://172.27.1.11:8123"

    zigbee2mqtt:
      loadBalancer:
        servers:
          - url: "http://zigbee2mqtt:8080"

tls:
  # Define default certificate to be the mqtt certificate because traefik can not match certificates to addresses like "mosquitto:1883" - it would need a complete domain to match the certificate
  # mqtt.mydomain.com is a special certificate for Tasmota (needs RSA - see https://dokuwiki.mydomain.com/computer/tasmota/start#mqtt)
  stores:
    default:
      defaultCertificate:
        certFile: /letsencrypt/live/mqtt.mydomain.com/fullchain.pem
        keyFile: /letsencrypt/live/mqtt.mydomain.com/privkey.pem
  certificates:
    - certFile: /letsencrypt/live/mydomain.com/fullchain.pem
      keyFile: /letsencrypt/live/mydomain.com/privkey.pem

This is just the relevant section of the Zigbee2Mqtt configuration.yaml:

permit_join: true
mqtt:
  base_topic: zigbee2mqtt.raspi06
  server: mqtt://mosquitto:1883
  user: client
  password: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  reject_unauthorized: false
  keepalive: 60
  version: 4
  include_device_information: true
serial:
  port: /dev/ttyUSB0
frontend:
  port: 8080
  url: https://zigbee2mqtt.mydomain.com

The relevant settings of mosquitto.conf:

listener 1883
socket_domain ipv4
2 Likes

Can you give some more details about this? I’m trying to do the same thing with Traefik, but I’m hitting two issues.

The first is even after setting up my password files, I can’t log in to zigbee2mqtt. It prompts me to login, but typing in the username/password combo I created is rejected. Do I need to something in the Auth_token entry box?

The second issue is for the TLS stores I set up the information for my domain but I’m told it fails to load x509 key pair. My Traefik container automatically pulls a cert when it’s created, is yours doing the same or are you saving a let’s encrypt key from another docker container and saving it in this location?

As you can see above, I have put the zigbee2mqtt credentials directly into the zigbee2mqtt config file and not into extra password files - I don’t need anything else i.e. not auth tokens.

I can’t remember Traefik pulling any other certificates on creation. The Let’s Encrypt certificates I use are created on another machine with a script and then the other machine’s directory is mounted via SMB and finally mounted into the docker.
When mounting the certificates into a container, make sure that there are no symlinks, which Docker usually can not resolve.

Hope that helps…

1 Like