ZHA vs Zigbee2MQTT After a Coordinator Swap: The Backup That Doesn’t Restore the Mesh
Ines Volk
September 18, 2026
I have a Home Assistant backup that restores every automation, every helper, and a Zigbee network that exists only as a list of ghosts. The new stick enumerates. ZHA says it is configured. Zigbee2MQTT opens the frontend and shows eighty devices with last-seen stamps from yesterday. None of them answer a brightness command. The backup did what it was written to do. It restored files. It did not put the same IEEE address and the same NVRAM onto a radio that is a different chip than the one I unplugged.
ZHA and Zigbee2MQTT fail this job in different rooms of the same house. People treat “I took a backup” as a mesh transplant. It is usually an application-state snapshot. The mesh still lives in the coordinator’s head, keyed to an IEEE the new dongle does not have.
What you actually copied
A Home Assistant Settings → System → Backup tarball includes ZHA’s bits in .storage — zha.storage, zigpy application state, sometimes an NVRAM blob if the integration was asked to make one. It includes Zigbee2MQTT’s config directory if you add that add-on folder: configuration.yaml, database.db, coordinator_backup.json when Z2M was healthy enough to write it. Restoring the tarball onto a new VM with the same stick in the same USB path is the happy path. I have done that after an SD card died. The mesh came back because the radio never left.
A coordinator swap is a different sentence. ConBee II to SkyConnect. ZBDongle-P to ZBDongle-E. USB stick to SLZB-06 on Ethernet. ZHA to Z2M on the same hardware. Each of those changes at least one of: radio family, IEEE, network key storage format, or the application that thinks it owns the PAN.
zigpy (under ZHA) and Z2M both know how to dump EmberZNet or zStack NVRAM. Those dumps are not a common language. An Ember backup will not flash onto a CC2652. A deCONZ / ConBee backup will not load into Z2M’s EZSP adapter block. A Z2M database.db will not become ZHA devices. I have tried enough of those combinations to stop calling the result a restore. It is a museum of names.
IEEE is the part the UI does not shout
Every Zigbee device that already joined remembers the coordinator’s IEEE and the network key. If you plug in a new Sonoff and start a fresh network — even with the same PAN ID you typed from memory — the devices still look for the old coordinator. Routers may show in a map if you somehow cloned the key and the PAN and not the IEEE. End devices will sit in a sulk. Interviews you run will create duplicates next to the ghosts.
Some adapters let you write an IEEE. The ZBDongle-E / other EFR32 sticks, and some CC2652 boards, can take the old address so the house believes the same radio sat down again. That write is a sharp tool. Two sticks with the same IEEE on the same air is how you get a mesh that looks haunted. I only write IEEE after the old stick is in a drawer, powered off, and I have confirmed the new stick’s firmware family matches the backup I am about to load.
ZHA’s radio migration wizard (the one that has improved a lot since 2023) is built around this idea: keep the network identity, move it onto a compatible adapter. “Compatible” is the word people skip. Ember to Ember often works. Ember to zStack is a rebuild. ConBee to anything is a rebuild unless you stay inside deCONZ’s own world, which I left on purpose.
Z2M will restore coordinator_backup.json onto a stick of the same protocol. If the JSON is missing because the add-on crashed before it wrote one, database.db is not a substitute. The database is who we used to talk to. The JSON is who we are. Restoring only the database onto a blank EZSP device gives you a frontend full of devices and a radio that started a new PAN. I have screenshotted that and still not learned the first time.

ZHA after a swap
If I stay on ZHA, I use the migration flow when the new radio is the same family. I take a zigpy backup from the old stick before I unplug it. I do not rely on last week’s HA snapshot as the only NVRAM copy. Snapshots are scheduled. The last good NVRAM might be older than the last device I paired.
If the new radio is a different family, I treat ZHA’s device list as a shopping list for re-pairing, not as state I will revive. I download the diagnostics, I print the IEEE and the area for each device, and I pair routers first — IKEA bulbs, Innr, the cheap Tuya repeats I regret — then the battery junk. Automations that key off device_id will break. Automations that key off entity_id survive if I rename the new entities to the old names. I prefer entity_id for that reason. I still get it wrong on the kitchen scene.
ZHA’s UI will cheerfully say the coordinator is ready on a SkyConnect that has never seen this house. That badge is USB enumeration plus a running zigpy app. It is not “the mesh is here.” I wait for a mains router I can see from the hallway to report a live LQI to the new stick. If that number never appears, I am not in the old network, and more waiting will not help.
Zigbee2MQTT after a swap
Z2M is more honest about files and easier to lie to yourself with. You can copy the whole /config/zigbee2mqtt folder onto a new machine, point port at tcp://slzb-06:6638, and watch the frontend light up. The frontend is the database. The radio is a socket. They do not automatically agree.
The sequence that has worked for me, same chip family:
- On the old stick, trigger a coordinator backup in Z2M. Confirm
coordinator_backup.jsonis fresh. - Note the IEEE, channel, and PAN ID from the frontend.
- Flash the new stick to a firmware Z2M actually supports. Do not skip this because the listing said “works with HA.”
- If the adapter allows it, write the old IEEE. Power down the old stick first.
- Restore the NVRAM / coordinator backup, then start Z2M against the existing
database.db. - Watch one known router. If it talks, I wait. If it does not, I stop before I start pairing duplicates.
The sequence that has never worked: restore an HA backup onto a new VM, plug in a different stick, and expect Z2M to “see” the mesh because the YAML still says channel: 15. Channel is a setting. It is not the network key.
Z2M also lets you change adapter from deconz to ezsp in YAML in thirty seconds. That is a new network wearing an old config file. I have done it at 11 p.m. because the ConBee locked up again. The morning was pairing. I should have started pairing at 11 p.m. and slept.

Crossing the stacks
ZHA to Z2M, or the other way, is not a restore. There is no converter I trust for a live mesh. zigpy and Z2M have both gotten better at backups inside their own world. They do not share a house key file you can import. I migrate by rebuilding. I pick a weekend. I do one floor. I keep the old stack running on the old stick until the new stack has the routers it needs, which means two coordinators on different channels if I can, or a very disciplined power-down of the old one if I cannot.
Two coordinators on the same channel with overlapping devices is how you get random joins and a week of “why does this bulb ignore Z2M.” If I must run them in parallel, they get different channels — 15 and 25 — and I accept the 2.4 GHz hit for a few days.
Hue stays Hue during this. I do not “restore” Hue into Z2M with a backup. I either leave the Bridge or I pair the bulbs over. A Hue backup file is not a Z2M database.
The backups that are worth keeping
I keep three things, not one tarball:
- A coordinator NVRAM dump from the running stack, dated, next to a note of IEEE / channel / firmware.
- The application database (
zha.storageor Z2M’sdatabase.dbplusconfiguration.yaml). - A human list of device model, location, and which ones are routers. The list is what I pair from when the first two disagree.
I test the NVRAM dump once on a spare stick of the same family before I need it. A backup you have never loaded is a rumor. I found that out when a “good” Z2M JSON restored into an EZSP stick that was still on a bootloader firmware the restore did not understand. The file was fine. The stick was not the stick I thought I bought.
Home Assistant’s backup is still required. It is how I get automations back. I stopped treating it as the Zigbee plan.
Trade-offs I will actually pick
Stay on ZHA if you want the migration wizard and you are willing to buy the next radio from a family zigpy already speaks well — SkyConnect / Connect ZBT-1, a known EZSP dongle. Stay on Z2M if you want to see the files and you will follow the restore order. Do not pick Z2M because a backup filename makes you feel safer. The filename is not the IEEE.
If I am changing radio families, I budget a re-pair and I do not sell the old stick until the new mesh has survived a week. The old stick is the only restore that always works: plug it back in, point the software at it, do not get clever.
Ethernet coordinators do not escape this. An SLZB-06 is a new IEEE unless you clone. It does escape USB 3 and a bad cable. It does not escape identity. I have migrated USB to Ethernet on the same CC2652 family with a backup and an IEEE write. I have also migrated USB to Ethernet as a Saturday of pairing because I got impatient and started Z2M “just to see.”
The backup that restores the mesh is the one that restores the radio’s identity onto a compatible radio, then the application state on top. Everything else restores a museum. ZHA and Zigbee2MQTT will both build you a very convincing museum. Walk one device before you trust the floorplan.