Zigbee2MQTT is one of those homelab tools that changes the shape of a smart home.

Instead of sending every bulb, plug, motion sensor, and switch through a vendor bridge, you run one local Zigbee coordinator, publish device state through MQTT, and let Home Assistant, Node-RED, ioBroker, Domoticz, or your own automations consume the messages.

Zigbee2MQTT lets you use Zigbee devices without the vendor bridge by translating Zigbee traffic into MQTT topics.

Zigbee2MQTT GitHub Source Code Zigbee2MQTT Documentation License: GPL-3.0 ❤️

What is Zigbee2MQTT?

Zigbee2MQTT is a bridge between a Zigbee network and an MQTT broker.

The hardware side is your Zigbee coordinator: usually a USB dongle or network-attached coordinator. The software side is Zigbee2MQTT: it talks to the coordinator, understands devices through zigbee-herdsman and zigbee-herdsman-converters, stores local state, and publishes device data into MQTT.

That makes MQTT the integration layer. Your automations do not need to know each vendor’s bridge API. They subscribe to topics and send commands through topics.

Repository Snapshot

I cloned the repository into:

tmp/zigbee2mqtt

Source state:

Item Value
Repository Koenkk/zigbee2mqtt
Commit 2b5a670ba219e18f5f77f6a038c8de444b87783d
Commit date 2026-06-01T20:28:15+02:00
Commit message chore(dev): release 2.11.0 (#31938)
Version 2.11.0
Package manager [email protected]
Node engines `^20.15.0
License GPL-3.0

Tech Overview of Zigbee2MQTT

Zigbee2MQTT is a TypeScript application compiled to dist/ and launched by index.js.

The runtime flow is:

Zigbee coordinator
  -> zigbee-herdsman
  -> Zigbee2MQTT controller
  -> extensions and state cache
  -> MQTT broker
  -> smart-home integrations

Important repo areas:

Path Purpose
lib/controller.ts Application lifecycle and extension startup
lib/zigbee.ts Coordinator, network, device, and group handling
lib/mqtt.ts MQTT connection, subscriptions, retained messages, publishing
lib/state.ts Device and group state cache
lib/extension/ Bridge, publish/receive, frontend, groups, OTA, health, availability, Home Assistant
lib/util/settings.ts Config loading, defaults, validation, migrations
data/configuration.example.yaml Example user config
docker/Dockerfile Official container image build

The project depends on:

  • zigbee-herdsman for coordinator/network communication
  • zigbee-herdsman-converters for device model mappings
  • mqtt for broker connectivity
  • ajv for settings validation
  • js-yaml for configuration files
  • zigbee2mqtt-frontend and zigbee2mqtt-windfront for web UI packages

Self-Hosting Zigbee2MQTT with Docker

For most homelabs, Docker is the clean path. You need three things:

  • a Zigbee coordinator
  • an MQTT broker
  • a writable Zigbee2MQTT data directory

Docker Compose Configuration

I added a Home-Lab Compose stack here:

Zigbee2MQTT Home-Lab Docker config

It includes Zigbee2MQTT plus Mosquitto:

services:
  mosquitto:
    image: eclipse-mosquitto:2
    container_name: zigbee2mqtt-mosquitto
    restart: unless-stopped
    volumes:
      - ./mosquitto.conf:/mosquitto/config/mosquitto.conf:ro
      - ./mosquitto-data:/mosquitto/data
      - ./mosquitto-log:/mosquitto/log
    ports:
      - "${MQTT_PORT:-1883}:1883"

  zigbee2mqtt:
    image: ghcr.io/koenkk/zigbee2mqtt:${ZIGBEE2MQTT_TAG:-latest}
    container_name: zigbee2mqtt
    restart: unless-stopped
    depends_on:
      - mosquitto
    volumes:
      - ./data:/app/data
      - /run/udev:/run/udev:ro
    devices:
      - ${ZIGBEE_ADAPTER:?Set ZIGBEE_ADAPTER in .env to your /dev/serial/by-id/... path}:/dev/ttyACM0
    ports:
      - "${ZIGBEE2MQTT_FRONTEND_PORT:-8080}:8080"
    environment:
      TZ: ${TZ:-Etc/UTC}
      ZIGBEE2MQTT_CONFIG_MQTT_SERVER: mqtt://mosquitto:1883
      ZIGBEE2MQTT_CONFIG_FRONTEND_ENABLED: "true"
      ZIGBEE2MQTT_CONFIG_SERIAL_PORT: /dev/ttyACM0

The important line is the required adapter mapping:

devices:
  - ${ZIGBEE_ADAPTER:?Set ZIGBEE_ADAPTER in .env to your /dev/serial/by-id/... path}:/dev/ttyACM0

Use the stable by-id path from:

ls -l /dev/serial/by-id

Do not casually use /dev/ttyACM0 or /dev/ttyUSB0 as the host path. Those can change after reboot or after plugging in another USB device.

Minimal Configuration

Copy the example configuration into the data directory:

cp data/configuration.example.yaml data/configuration.yaml

The generated example uses the included Mosquitto service:

mqtt:
  base_topic: zigbee2mqtt
  server: mqtt://mosquitto:1883

serial:
  port: /dev/ttyACM0

frontend:
  enabled: true

If Zigbee2MQTT cannot auto-detect your adapter type, add it explicitly:

serial:
  port: /dev/ttyACM0
  adapter: zstack

Common adapter values include zstack, ember, deconz, zigate, and zboss.

Starting the Stack

Create your .env:

cp .env.sample .env

Set:

ZIGBEE_ADAPTER=/dev/serial/by-id/your-real-coordinator

Then start:

docker compose up -d

Open the frontend:

http://localhost:8080

Field Note: What I Validated Locally

This local pass validated the repo and Compose configuration. It did not start a live Zigbee network.

Local environment:

Item Value
Docker 29.5.3
Docker Compose v5.1.4
Node v22.22.0
pnpm 10.33.0
Free disk about 13GB
Available RAM about 4.6GiB
Swap 0B
Zigbee coordinator Not present; /dev/serial/by-id does not exist

Home-Lab compose validation:

ZIGBEE_ADAPTER=/dev/serial/by-id/replace-with-your-zigbee-coordinator docker compose config

Site snippet validation:

ZIGBEE_ADAPTER=/dev/serial/by-id/replace-with-your-zigbee-coordinator docker compose -f assets/snippets/zigbee2mqtt/docker-compose.yml config

Both rendered successfully.

I did not run docker compose up because this machine has no coordinator device to pass through. A live test without the radio would only produce the expected adapter startup failure.

Home Assistant and Other Integrations

Home Assistant is the common pairing, but it is not the only one. Zigbee2MQTT speaks MQTT, so anything that can consume MQTT can integrate with it.

For Home Assistant discovery, enable:

homeassistant:
  enabled: true

For a dedicated Home Assistant stack, I prefer keeping Mosquitto, Zigbee2MQTT, and Home Assistant as separate services. That makes upgrades and troubleshooting cleaner: MQTT broker health, coordinator access, and Home Assistant automation issues can be isolated.

Practical Setup Notes

Use a USB extension cable for the coordinator. Keep it away from the server chassis, SSDs, USB 3 noise, and WiFi access points.

Pick your Zigbee channel deliberately. Zigbee uses 2.4 GHz, so WiFi channel planning matters.

Back up the data/ directory. It contains configuration.yaml, database.db, coordinator backup files, generated network keys, device names, groups, and runtime state.

Keep permit_join off except while pairing devices. Leaving a Zigbee network open is avoidable risk.

Building From Source

For development:

git clone https://github.com/Koenkk/zigbee2mqtt.git
cd zigbee2mqtt
pnpm install --include=dev
pnpm run build
pnpm run start

Useful checks:

pnpm run check:w
pnpm run test:coverage

Conclusion

Zigbee2MQTT is the clean local-first way to make Zigbee devices speak MQTT. It works well when you want control over your coordinator, broker, naming, discovery, automations, and backups instead of handing those concerns to a vendor bridge.

The deployment is straightforward, but it is hardware-sensitive. The difference between a stable setup and a frustrating one is usually the coordinator path, adapter type, radio placement, MQTT reachability, and Zigbee/WiFi channel planning.

FAQ