Zigbee2MQTT turns a USB Zigbee coordinator into an MQTT gateway: every sensor, bulb and switch paired to it shows up as a JSON message on a topic, and every topic ending in /set becomes a command. It does not ship a broker — you point it at one in configuration.yaml, and any standards-compliant MQTT broker works: a local Mosquitto, the Home Assistant add-on, or a managed broker in the cloud.
This tutorial configures Zigbee2MQTT (2.x) against an external broker, explains the topic layout, and shows how to verify the connection and control a device from the command line.
How the pieces fit
Zigbee device ──radio──▶ Coordinator (USB) ──serial──▶ Zigbee2MQTT ──MQTT──▶ Broker ──▶ Home Assistant / Node-RED / your appZigbee2MQTT is just another MQTT client. That has two practical consequences: the broker can live anywhere reachable over the network, and anything else that speaks MQTT — a dashboard, a script, the online client — sees exactly the same messages your home-automation software sees.
Minimal configuration
Zigbee2MQTT reads data/configuration.yaml. The MQTT section is the part that connects it to your broker:
mqtt:
base_topic: testmqtt/z2m # default is "zigbee2mqtt"
server: mqtts://broker.connectmq.com:8883
user: '!secret user'
password: '!secret password'
client_id: z2m-living-room
keepalive: 60
version: 5 # 3, 4 (3.1.1) or 5
serial:
port: /dev/ttyACM0
adapter: ember # zstack, ember, deconz, zigate, zboss
frontend:
enabled: true
port: 8080
homeassistant:
enabled: false # true if Home Assistant should auto-discover devices
advanced:
log_level: infouser: username
password: passwordNotes on the important keys:
server—mqtt://host:1883for plain TCP,mqtts://host:8883for TLS. WebSocket URLs (wss://host:8084/mqtt) also work because Zigbee2MQTT uses MQTT.js under the hood, but TCP is the normal choice for a gateway.base_topic— the root of everything Zigbee2MQTT publishes. Keep the defaultzigbee2mqtton a private broker; on a shared test broker use a unique root like the one above so you do not collide with other users.client_id— must be unique on the broker. Two instances with the same ID will kick each other off in a loop; see client ID best practices.ca,key,cert— only needed for a private CA or mutual TLS. A broker with a publicly trusted certificate works withmqtts://alone. Never "fix" certificate errors withreject_unauthorized: falsein production.!secret— keeps credentials insecret.yamlso you can share or commitconfiguration.yamlsafely.
Every option can also be set via environment variables, which is convenient in containers: ZIGBEE2MQTT_CONFIG_MQTT_SERVER, ZIGBEE2MQTT_CONFIG_MQTT_USER, ZIGBEE2MQTT_CONFIG_MQTT_PASSWORD, and so on.
Running it with Docker
services:
zigbee2mqtt:
image: ghcr.io/koenkk/zigbee2mqtt
restart: unless-stopped
volumes:
- ./data:/app/data
- /run/udev:/run/udev:ro
ports:
- "8080:8080"
environment:
- TZ=Europe/Berlin
devices:
- /dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus-if00-port0:/dev/ttyACM0Map the coordinator by its /dev/serial/by-id/... path: /dev/ttyUSB0 can change after a reboot or when another USB device is plugged in. If you run a local broker in the same Compose file, refer to it by service name (mqtt://mosquitto:1883), not localhost — inside a container localhost is the container itself. The Docker Compose stack guide covers that setup.
Start it and watch the log:
docker compose up -d
docker compose logs -f zigbee2mqttA healthy start logs Connecting to MQTT server at mqtts://broker.connectmq.com:8883 followed by Connected to MQTT server. If you see MQTT error: Connection refused: Not authorized or MQTT failed to connect, exiting..., check credentials and the port/scheme pair first — the Zigbee2MQTT errors guide walks through each message.
The topic layout
| Topic | Direction | Payload |
|---|---|---|
<base>/bridge/state | Z2M → broker (retained) | {"state":"online"} / {"state":"offline"} (Last Will) |
<base>/bridge/info | Z2M → broker (retained) | Version, coordinator, config |
<base>/bridge/devices | Z2M → broker (retained) | All paired devices with capabilities |
<base>/bridge/event | Z2M → broker | Device joined, interview finished, left |
<base>/bridge/request/... | You → Z2M | Admin requests (permit join, rename, remove) |
<base>/bridge/response/... | Z2M → you | Result of each request |
<base>/<friendly_name> | Z2M → broker | Device state as JSON |
<base>/<friendly_name>/set | You → Z2M | Commands as JSON |
<base>/<friendly_name>/get | You → Z2M | Ask the device to report a value |
<base>/<friendly_name>/availability | Z2M → broker (retained) | {"state":"online"} when availability is enabled |
New devices are named by their IEEE address (0x00158d0001a2b3c4). Rename them to something meaningful; a slash in the name creates a hierarchy, so living_room/ceiling becomes testmqtt/z2m/living_room/ceiling. That is the easiest way to get a clean smart home topic tree.
bridge/state, bridge/info and bridge/devices are retained messages, so any client that subscribes later immediately learns whether the bridge is up and which devices exist.
Test every step from the command line
1. Is the bridge connected? Subscribe to the bridge state; the retained value arrives instantly:
mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u "$MQTT_USER" -P "$MQTT_PASS" -t 'testmqtt/z2m/bridge/state' -v
# testmqtt/z2m/bridge/state {"state":"online"}2. Watch all traffic while you press a button or open a door sensor:
mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u "$MQTT_USER" -P "$MQTT_PASS" -t 'testmqtt/z2m/#' -v3. Pair a device by opening the network for a limited time (Zigbee2MQTT 2.x requires time; 0 closes it again):
mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u "$MQTT_USER" -P "$MQTT_PASS" \
-t 'testmqtt/z2m/bridge/request/permit_join' -m '{"time": 120}'The answer arrives on bridge/response/permit_join, and bridge/event reports device_joined and device_interview as the device pairs.
4. Rename and control it:
mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u "$MQTT_USER" -P "$MQTT_PASS" -t 'testmqtt/z2m/bridge/request/device/rename' \
-m '{"from": "0x00158d0001a2b3c4", "to": "living_room/ceiling"}'
mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u "$MQTT_USER" -P "$MQTT_PASS" -t 'testmqtt/z2m/living_room/ceiling/set' \
-m '{"state": "TOGGLE", "brightness": 180, "transition": 1}'The bulb's new state appears on testmqtt/z2m/living_room/ceiling within a second. If a command does nothing, check bridge/logging — Zigbee2MQTT logs rejected payloads there.
You can run the same steps in the browser: open the online MQTT client, subscribe to testmqtt/z2m/# and publish to the /set topics from the publish panel.
Watch your Zigbee2MQTT topics live
Integrating with Home Assistant
Set homeassistant: enabled: true and Zigbee2MQTT publishes discovery messages under homeassistant/.... Home Assistant's MQTT integration must point at the same broker; devices then appear automatically with all their entities. If you changed base_topic, nothing else needs to change — the discovery payloads include the right state and command topics. See Home Assistant and MQTT for the broker side.
Production checklist
- Give Zigbee2MQTT its own broker user and restrict it to
<base>/#plushomeassistant/#if discovery is on. - Use TLS (
mqtts://) whenever the broker is not on the same host. - Enable
availabilityso offline devices are reported instead of silently stale. - Keep
permit_joinclosed except while pairing. - Back up the whole
data/directory — it contains the network key and the coordinator backup, not just the config.
Zigbee2MQTT is the simplest way to free Zigbee devices from vendor hubs: one YAML block points it at any broker, and from then on every device is just a topic. Verify the bridge state first, watch the traffic while you pair, and control devices with plain mosquitto_pub before you build automations on top.
Frequently asked questions
Does Zigbee2MQTT include its own MQTT broker?
No. Zigbee2MQTT is an MQTT client: it translates Zigbee radio traffic into MQTT messages and needs an external broker, such as Mosquitto, the Home Assistant Mosquitto add-on or a managed broker.
Can Zigbee2MQTT connect to a broker over TLS?
Yes. Set mqtt.server to mqtts://host:8883. Brokers with a publicly trusted certificate need nothing else; for a private CA add the ca option, and for mutual TLS add key and cert.
How do I control a Zigbee device through MQTT?
Publish JSON to <base_topic>/<friendly_name>/set, for example {"state": "TOGGLE"} or {"brightness": 128}. The device's new state is published on <base_topic>/<friendly_name>.


