There is a specific kind of dread that comes with re-pairing a Zigbee device. Not the pairing itself — that part usually works. The dread is about everything around it: the friendly name you picked two years ago and that eleven automations now depend on, the debounce you tuned, the temperature offset you worked out by holding a reference thermometer against the wall for ten minutes, the retain flag, the group membership. Remove the device, and Zigbee2MQTT throws all of it away. Pair it again and you get a fresh 0x00158d0004a1b2c3 with default everything, plus a quiet trail of broken automations you will discover over the following week.
That has been the deal for as long as I have run Zigbee2MQTT. It is the reason I have left a dying wall switch in place for months rather than touch it. Zigbee2MQTT 2.13.0 and 2.14.0 quietly changed the deal, and almost nobody noticed, because both changes landed as one-line entries in release notes dominated by device converters.
What actually changed
Zigbee2MQTT 2.14.0, released on 1 September 2026, added a keep_config flag to the device remove request. The pull request describes it plainly: previously, device removal always deleted the stored device configuration; now the remove request can explicitly keep that configuration intact. The device leaves the network. Its settings stay in your database, waiting.
In practice that is a single extra key in the MQTT payload you send to zigbee2mqtt/bridge/request/device/remove:
{"id": "kitchen/ceiling_switch", "keep_config": true}
Pair the same physical device again, and it comes back as itself: same friendly name, same options, same offsets. No renaming, no re-tuning, no automation archaeology.
A month earlier, 2.13.0 had added the mirror-image option: clear_cache. That one throws more away, deliberately. It wipes the cached interview data so the next join starts from a clean slate with a full interview from scratch. The PR is explicit about why this matters — it removes the need to restart in what the project used to call the “remove/restart/re-pair” dance. If you have ever been told on a forum to restart Zigbee2MQTT before re-pairing a device that refuses to interview properly, that restart was working around exactly this cache.
So the remove request now has four modifiers, and they are not interchangeable:
keep_config— device leaves the network, its saved configuration survives. Use when you know you will re-pair it.clear_cache— forget the device completely, including cached interview data. Use when a device is behaving strangely or when you are developing firmware for one.force— remove from the database only. The device still holds the network key until it is factory reset, and can still talk on your network.block— ask the device to leave again immediately if it ever tries to rejoin.
The documentation covers all four, and the bridge response now echoes every flag back, so you can confirm what actually happened rather than guessing.

The catch, and it is a real one
At the time 2.14.0 shipped, keep_config is an MQTT-level flag. The pull request closes with “Next steps: Update frontend with a toggle to activate this.” If you do all your device management through the Zigbee2MQTT web UI — which is most people, reasonably — the remove button still behaves the old way. The feature exists, but you have to reach past the interface to use it.
That means one mosquitto_pub command, or a two-line script action in Home Assistant that publishes to the bridge topic. It is not hard. It is just not discoverable, which is why a genuinely useful change is sitting unnoticed in a changelog full of Tuya converters. I would rather write this post than let it stay there.
Two other things in this release cycle worth knowing
Covers went upside down, briefly. Two days after 2.14.0, 2.14.1 shipped as a hotfix for several issues where cover states were inverted. Open read as closed, buttons did the opposite of what they said. If you run blinds or shutters over Zigbee and you updated on release day, that was not your wiring. Go to 2.14.1. I mention it because this is the honest shape of running a fast-moving open-source stack: a release every month, an occasional two-day correction, and a maintainer who ships the fix rather than pretending it did not happen. I will take that trade over a vendor cloud that silently changes behaviour and tells me nothing.
Node.js 20 is gone. 2.13.0 dropped support for Node.js 20 because it reached end of life. If you run Zigbee2MQTT bare metal, you need Node.js 24 or 26. If you run it as a Home Assistant add-on or in the official Docker image — as I do on the EliteDesk — this is handled for you and you will never notice. Bare-metal installs are the ones that will fail to start with an unhelpful error if you skip this.
While we are on the subject of things that changed under bare-metal installs: since 2.11.0 in June, external extensions and external converters are disabled by default for new installations. Existing setups keep working. New ones need enable_external_js set in the advanced section of the configuration. This is the right default — an external converter is arbitrary JavaScript executing inside the process that holds your Zigbee network key — but it does mean a fresh install will silently ignore a converter you copied over from an old one.
Why this is the kind of change I care about
Nothing here is exciting. There is no new protocol, no new device class, no announcement video. keep_config is a boolean in a JSON payload.
But the friction in a self-hosted smart home is almost never the big architectural stuff. It is the accumulated cost of small irreversible operations — the ones where a routine maintenance task quietly destroys configuration you spent real time producing, and you only find out three automations later. That cost is what makes people stop maintaining their setup. It is what makes a dying switch stay on the wall for six months.
A flag that lets me swap a failing device without losing its identity is worth more to me than another dashboard card. It makes the system feel less like a house of cards and more like something I am allowed to work on. That is the whole point of running this locally.
The switch comes off the wall this weekend.

Leave a Reply