Matter-over-Thread commissioning never reaches Matter Server when using HA in Docker

I’m running into an issue when trying to commission Matter-over-Thread devices using Home Assistant in Docker with an external OTBR and Matter Server containers + ZBT-1 (Thread RCP).
Going through the add matter device flow in HA app I get stuck on the “Checking thread network connectivity”.

I see messages popping up in the OTBR container during the commissioning, but the Matter Server logs are completely silent.

My setup:

  • Raspberry PI 4 running RPi OS with :
    • HA in a docker container
    • OTBR and Matter Server containers in one stack
  • S24 (android) with the HA app

I’ve enabled ipv6 on my router and I see ipv6 addresses across the relevant devices.

I’ve also tried with a simulated Matter device and I was able to connect it to HA via the matter server, and I could see all the logs entries there.

I don’t know what to try next.

I’m not very much familiar yet with the OTBR logs but here is part of the logs while I was trying to commission the new device:

02:20:25.074 [I] Mle-----------: Send Announce on channel 20
02:20:25.074 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:4, cmd:PROP_VALUE_SET, key:STREAM_RAW, len:82, channel:20, maxbackoffs:4, maxretries:15 ...
02:20:25.074 [D] P-SpinelDrive-: ... csmaCaEnabled:1, isHeaderUpdated:1, isARetx:0, skipAes:1, txDelay:0, txDelayBase:0
02:20:25.084 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:4, cmd:PROP_VALUE_IS, key:LAST_STATUS, status:OK
02:20:25.084 [I] MeshForwarder-: Sent IPv6 UDP msg, len:83, chksum:cb67, ecn:no, to:0xffff, sec:yes, prio:net, radio:all
02:20:25.084 [I] MeshForwarder-:     src:[fe80:0:0:0:404:a85a:ca3f:7c6a]:19788
02:20:25.084 [I] MeshForwarder-:     dst:[ff02:0:0:0:0:0:0:1]:19788
02:20:25.084 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:5, cmd:PROP_VALUE_SET, key:PHY_CHAN, channel:22
02:20:25.084 [D] P-RadioSpinel-: Wait response: tid=5 key=33
02:20:25.087 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:5, cmd:PROP_VALUE_IS, key:PHY_CHAN, channel:22
02:20:27.296 [I] Mle-----------: Send Advertisement (ff02:0:0:0:0:0:0:1)
02:20:27.296 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:6, cmd:PROP_VALUE_SET, key:STREAM_RAW, len:69, channel:22, maxbackoffs:4, maxretries:15 ...
02:20:27.296 [D] P-SpinelDrive-: ... csmaCaEnabled:1, isHeaderUpdated:0, isARetx:0, skipAes:0, txDelay:0, txDelayBase:0
02:20:27.305 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:6, cmd:PROP_VALUE_IS, key:LAST_STATUS, status:OK
02:20:27.306 [I] MeshForwarder-: Sent IPv6 UDP msg, len:90, chksum:764a, ecn:no, to:0xffff, sec:no, prio:net, radio:all
02:20:27.306 [I] MeshForwarder-:     src:[fe80:0:0:0:404:a85a:ca3f:7c6a]:19788
02:20:27.306 [I] MeshForwarder-:     dst:[ff02:0:0:0:0:0:0:1]:19788
02:20:41.656 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:0, cmd:PROP_VALUE_IS, key:STREAM_RAW, len:10, rssi:-87 ...
02:20:41.656 [D] P-SpinelDrive-: ... noise:-128, flags:0x0000, channel:22, lqi:12, timestamp:8640034771, rxerr:0
02:20:41.656 [I] Mac-----------: Received Beacon Request
02:20:41.656 [I] Mac-----------: Sending Beacon
02:20:41.656 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:7, cmd:PROP_VALUE_SET, key:STREAM_RAW, len:15, channel:22, maxbackoffs:4, maxretries:15 ...
02:20:41.656 [D] P-SpinelDrive-: ... csmaCaEnabled:1, isHeaderUpdated:0, isARetx:0, skipAes:0, txDelay:0, txDelayBase:0
02:20:41.662 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:7, cmd:PROP_VALUE_IS, key:LAST_STATUS, status:OK
02:20:42.182 [I] Mle-----------: Send Advertisement (ff02:0:0:0:0:0:0:1)
02:20:42.183 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:8, cmd:PROP_VALUE_SET, key:STREAM_RAW, len:69, channel:22, maxbackoffs:4, maxretries:15 ...
02:20:42.183 [D] P-SpinelDrive-: ... csmaCaEnabled:1, isHeaderUpdated:0, isARetx:0, skipAes:0, txDelay:0, txDelayBase:0
02:20:42.192 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:8, cmd:PROP_VALUE_IS, key:LAST_STATUS, status:OK
02:20:42.192 [I] MeshForwarder-: Sent IPv6 UDP msg, len:90, chksum:2b6d, ecn:no, to:0xffff, sec:no, prio:net, radio:all
02:20:42.192 [I] MeshForwarder-:     src:[fe80:0:0:0:404:a85a:ca3f:7c6a]:19788
02:20:42.192 [I] MeshForwarder-:     dst:[ff02:0:0:0:0:0:0:1]:19788
02:20:46.637 [I] Mle-----------: Send Announce on channel 21
02:20:46.638 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:9, cmd:PROP_VALUE_SET, key:STREAM_RAW, len:82, channel:21, maxbackoffs:4, maxretries:15 ...
02:20:46.638 [D] P-SpinelDrive-: ... csmaCaEnabled:1, isHeaderUpdated:1, isARetx:0, skipAes:1, txDelay:0, txDelayBase:0
02:20:46.648 [D] P-SpinelDrive-: Received spinel frame, flg:0x2, iid:0, tid:9, cmd:PROP_VALUE_IS, key:LAST_STATUS, status:OK
02:20:46.648 [I] MeshForwarder-: Sent IPv6 UDP msg, len:83, chksum:f087, ecn:no, to:0xffff, sec:yes, prio:net, radio:all
02:20:46.648 [I] MeshForwarder-:     src:[fe80:0:0:0:404:a85a:ca3f:7c6a]:19788
02:20:46.648 [I] MeshForwarder-:     dst:[ff02:0:0:0:0:0:0:1]:19788
02:20:46.648 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:10, cmd:PROP_VALUE_SET, key:PHY_CHAN, channel:22
02:20:46.648 [D] P-RadioSpinel-: Wait response: tid=10 key=33

From the matter server, I was able to enable DEBUG log level, and the only thing I see is this:

2025-11-20 18:37:17.508 (MainThread) DEBUG [matter_server.server.client_handler] [4463196016] Connected from 192.168.1.10
2025-11-20 18:37:17.513 (MainThread) DEBUG [matter_server.server.client_handler] [4463196016] MSG received: WSMessage(type=<WSMsgType.TEXT: 1>, data='{\n  "message_id": "aa790d4e5fcc4fad9a4882f9bcf24067",\n  "command": "start_listening",\n  "args": null\n}', extra='')
2025-11-20 18:37:17.518 (MainThread) DEBUG [matter_server.server.client_handler] [4463196016] MSG received: WSMessage(type=<WSMsgType.TEXT: 1>, data='{\n  "message_id": "a4126042878348cf866267a3e4486e77",\n  "command": "set_default_fabric_label",\n  "args": {\n    "label": "Home"\n  }\n}', extra='')

the HA is running on 192.168.1.10

Any suggestion?

How is docker networking? Must be host or macvlan.

Commissioning device(phone), HA and matter server must all be on same network vlan and accessible. Must commision using SSID you want matter device to use. Since its thread pretty sure OTBR must be on that network as well.

They are all as host.

Must commision using SSID you want matter device to use.

well, the device I’m trying to configure (EVE blinds) is using thread, so I assume the SSID wouldn’t be important there.

I’ve done the credential sync via the HA companion app config as well.

One thing I couldn’t check yet is if the blinds I got do not support matter from the factory, needing a FW upgrade.

I’m kind of guessing, but you are probably using Android HA Companion App, and when the CA tries to get the device to join the Thread network the Companion App waits to see mDNS advertisements for that device show up on the LAN/WiFi side of things (via IPv6), and my guess is that “Checking thread network connectivity” means it is not seeing these advertisements. It could mean that the device was given the incorrect Thread credentials which means the OTBR won’t be advertising it, or it means the mobile device is not setup for IPv6.

SOLVED - It was IPv6 forwarding

After a lot of troubleshooting I found the issue. The problem wasn’t with Thread credentials, mDNS, or the OTBR setup.

The root cause: IPv6 forwarding was disabled on the host

Even though OTBR was working fine as Thread border router, routes to Thread network existed (wpan0 interface had the correct prefixes), Thread dataset was synced between OTBR and HA, and devices were joining the Thread network successfully, the Linux kernel wasn’t forwarding packets between interfaces because /proc/sys/net/ipv6/conf/all/forwarding was set to 0.

This meant HA couldn’t actually reach Thread devices for the connectivity check, so commissioning would get stuck at “Checking connectivity to Thread network”.

The fix:

sudo sh -c 'echo 1 > /proc/sys/net/ipv6/conf/all/forwarding'

To make it permanent add to /etc/sysctl.conf:

net.ipv6.conf.all.forwarding = 1

Then sudo sysctl -p

After enabling forwarding, commissioning worked immediately through the HA companion app.

Docker OTBR can’t enable this itself even with network_mode: host since it runs in a container. This needs to be configured at the host OS level.

1 Like