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-herdsmanfor coordinator/network communicationzigbee-herdsman-convertersfor device model mappingsmqttfor broker connectivityajvfor settings validationjs-yamlfor configuration fileszigbee2mqtt-frontendandzigbee2mqtt-windfrontfor 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
Pre-Requisites - Docker
Install Docker on your system before proceeding:
- Linux: Official Docker Engine install guide
- Windows / Mac: Docker Desktop
Verify installation: docker --version && docker compose version
Docker Compose Configuration
I added a Home-Lab Compose stack here:
Zigbee2MQTT Home-Lab Docker configIt 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
Do I need an MQTT broker for Zigbee2MQTT?
Can I run Zigbee2MQTT without a Zigbee coordinator?
Why use /dev/serial/by-id instead of /dev/ttyACM0?
ttyACM0 or ttyUSB0 path can change after reboot or when another USB serial device is connected.
Does Zigbee2MQTT replace Home Assistant?
Where is Zigbee2MQTT state stored in Docker?
/app/data. In the compose stack from this post, that maps to ./data.
Comments