i have a raspberry pi 5, running raspbian-os (Debian GNU/Linux 13 (trixie)).
Just quick view from my side. I had a problem with ipv6 forwarding. (causes new matter devices refuseses to join)
i have this setting in /etc/sysctl.d/99-thread-routing.conf (change end0 to you network)
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
net.ipv6.conf.default.forwarding=1
net.ipv6.conf.all.accept_ra=2
net.ipv6.conf.end0.accept_ra=2
net.ipv6.conf.all.accept_ra_rt_info_max_plen=64
net.ipv6.conf.end0.accept_ra_rt_info_max_plen=64
(Also be carefull of order - as i undersend all can rewrite my end0 network)
but it keeps changing sysctl net.ipv6.conf.end0.accept_ra to 0 after RP5 reboot.
when i just
sudo sysctl --system
then restart threadborder container its starts to work.
And i can ping ipv6 devices throught my network => matter devices are able to connect.
any ideas why?
Stack: RP5 , openmediavalut,
docker dcontainers: HA, openthread/border-router:latest, matter, ZBT-2.
I have this compose setup
otbr:
image: ghcr.io/ownbee/hass-otbr-docker:latest
container_name: otbr
restart: unless-stopped
privileged: true
network_mode: host
cap_add:
- SYS_ADMIN
- NET_ADMIN
devices:
- /dev/serial/by-id/${zbt-2_serial}:/dev/${zbt-2}
- /dev/net/tun:/dev/net/tun
volumes:
- ${DOCKER}/otbr_data:/var/lib/thread
environment:
- TZ=${TZONE}
- DEVICE=/dev/${zbt-2}
- BACKBONE_IF=wlp0s20f3
- FLOW_CONTROL=1
- FIREWALL=1
- NAT64=1
- BAUDRATE=460800
- OTBR_REST_PORT=8081
- OTBR_WEB_PORT=8083
- AUTOFLASH_FIRMWARE=0
labels:
- ***
matter-server:
image: ghcr.io/home-assistant-libs/python-matter-server:stable
container_name: matter-server
restart: unless-stopped
privileged: true
security_opt:
- apparmor:unconfined
network_mode: host
volumes:
- ${DOCKER}/matter_server:/data # Persistent storage
- /run/dbus:/run/dbus:ro # For Bluetooth/DBus access
environment:
- TZ=${TZONE}
- MATTER_LOG_LEVEL=DEBUG
- CHIP_IFACE=wlp0s20f3
- MATTER_SERVER__PRIMARY_INTERFACE=wlp0s20f3
command: --storage-path /data --primary-interface wlp0s20f3
ports:
- "5580:5580" # debugging
labels:
- ***
With
net.ipv4.conf.all.forwarding = 1
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.all.accept_ra_rt_info_max_plen = 0
net.ipv6.conf.all.accept_ra = 2
net.ipv6.conf.wlp0s20f3.forwarding = 1
net.ipv6.conf.wlp0s20f3.accept_ra = 0
net.ipv6.conf.wlp0s20f3.accept_ra_rt_info_max_plen = 0
So far, it works fine with a caveat - from time to time, I just get random N/A for my IKEA Matter devices - fixed easily by otbr restart that is scripted this day. Still have to find why.
@sphinxbox There are some known issues with Ikea devices, see this thread on matter.js. Ikea is aware and working on firmware updates. Maybe that fixes your issues.
Sietse
Thank you very much for this very helpful post.
I’d like to share a solution to the only thing I had struggled with - to keep IPv6 forwarding persistent over reboot on my Raspberry Pi OS.
In addition to the script from openthread.io mentioned in the OP, I had to add instructions to /etc/dhcpcd.conf to leave IPv6 alone - otherwise it was overwriting the sysctl accept_ra and accept_ra_rt_info_max_plen.
This is documented at Thread Border Router - Bidirectional IPv6 Connectivity and DNS-Based Service Discovery | OpenThread, but it might be helpful for others as well to point it out in the OP.
Can you give me the steps you had to take? I will incorporate them in the OP.
Sietse
Sure thing. I used the following section from Thread Border Router - Bidirectional IPv6 Connectivity and DNS-Based Service Discovery | OpenThread :
If you are trying to reconnect Wi-Fi or restart Ethernet interface, make sure that dhcpcd is not used for managing your WiFi-/Ethernet IPv6 network. Because dhcpcd always overrides the
accept_raoption everytime the interface is restarted and youraccept_raconfiguration will be lost.Append below lines to the dhcpcd configuration file (e.g.
/etc/dhcpcd.conf) to explicitly disable IPv6 in dhcpcd:noipv6 noipv6rsYou need to reboot to take the change into effect.
hey all. i reinstalled the home-assistant app recently on my iphone and now i cant pair any new IKEA accessories. here is what AI said the following…
anyone having similar issue? while my accessories paired before the app reinstall still works fine. but i cant pair any new ones. )=
never a dull moment with this setup!!! ![]()
Theory: older iOS accepted OTBR’s _meshcop._udp as valid Thread Border Router. Newer iOS (post iOS 17/18) requires Apple-certified TBR only.
App reinstall likely triggered iOS/app update → stricter TBR validation → OTBR now rejected.
What iOS version running on iPhone 13 mini?
❯ 26.3.1 ios
iOS 26 = root cause. Apple tightened Thread TBR requirement in iOS 17+ and further in iOS 26. OTBR was accepted as TBR in older iOS. iOS 26 requires Apple-certified
hardware TBR only.
Reinstall triggered iOS 26 update → broke OTBR compatibility.
No server-side fix. Options unchanged:
1. HomePod mini (~$99) — Apple TBR
2. Android phone — bypasses Apple stack entirely
thanks for sharing all the steps, super helpful! i remember getting stuck on the ipv6 forwarding part too, took me a while to figure out the interface thing (mine wasn’t called eno1 either). did you notice any weirdness with thread devices dropping off after a reboot? i had to double check my docker permissions, just in case. anyway, glad you got it running!
turns out it was firewall. i guess i must have manually ran the firewall steps and not set it permanently during bootup. ![]()
Just reporting back in that my Thread network has been running solid with 0 drop outs for months now. I only have IKEA Myggspray devices connected for now.
The only small issue that I have is that I have to re-select my border router in the thread integration after every full server restart. But that is only once a month so that is a non issue. It survives restarts and docker upgrades.
I'm stuck on configuring the IPv6 forwarding and RA acceptance. I'm using Debian on a Raspberry Pi 5, no dhcpcd as far as I can tell, since I don't have /etc/dhcpcd.conf or a dhcpcd service, just systemd-networkd. I have the following set:
└[~]> cat /etc/sysctl.d/60-otbr-accept-ra.conf
net.ipv6.conf.eth0.accept_ra = 2
net.ipv6.conf.eth0.accept_ra_rt_info_max_plen = 64
and
└[~]> cat /etc/sysctl.d/60-otbr-ip-forward.conf
net.ipv6.conf.all.forwarding = 1
net.ipv4.ip_forward = 1
I also set
└[~]> cat /etc/systemd/networkd.conf
[Network]
...
IPv6Forwarding=yes
IPv6AcceptRA=yes
But after a reboot, accept_ra and accept_ra_rt_info_max_plen are at 0 again. If I run sudo sysctl --system, it applies the configs for the session, but something is clobbering the settings on reboot, and I'm not sure what it is.
Other than these items, everything seems fine. I can see the ZBT-2 adapter connecting fine in otbr, I can see my Matter and Thread configs in HA are fine and the Open Thread border router is fine, it's just that when I try to connect a device using my Android phone, it spins for a bit before it tells me that the device requires a Thread border router. I assume that's because the RA acceptance and forwarding aren't sticking, just because that's the only thing I haven't managed to get working.
I feel like I'm missing something silly. Does anyone have any ideas?
EDIT: I solved it, and it was silly. I was doing Settings → Thread → Send credentials to Home Assistant when I needed to be doing Settings → Companion App → Troubleshooting → Sync Thread credentials. Once I did that, I managed to sync a device!
I do not know what I am doing wrong, but I cannot start OTBR. I have flashed as instructed by OP to the latest Thread firmware:
name: Border-router
services:
border-router:
image: openthread/border-router:latest
container_name: otbr
environment:
OT_RCP_DEVICE: "spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=460800"
OT_INFRA_IF: "ens18"
OT_THREAD_IF: "wpan0"
OT_LOG_LEVEL: 7
OT_REST_LISTEN_ADDR: 0.0.0.0
OT_REST_LISTEN_PORT: 8981
OT_WEB_LISTEN_ADDR: 0.0.0.0
OT_WEB_LISTEN_PORT: 8980
TZ: Europe/Berlin
network_mode: host
cap_add:
- NET_ADMIN
devices:
- /dev/serial/by-id/usb-Nabu_Casa_ZBT-2_E072A1D8B4B0-if00:/dev/ttyACM0
- /dev/net/tun
volumes:
- /var/lib/otbr:/data
restart: unless-stopped
But it crashes with the following:
Starting otbr-agent...
[NOTE]-AGENT---: Running 0.3.0-a654913
[NOTE]-AGENT---: Thread version: 1.4.0
[NOTE]-AGENT---: Thread interface: wpan0
[NOTE]-AGENT---: Radio URL: spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=460800
[NOTE]-AGENT---: Radio URL: trel://ens18
[NOTE]-ILS-----: Infra link selected: ens18
00:00:00.000 [C] P-RadioSpinel-: RCP => [C] Platform------: Reset info: 0x4 (PWR)
00:00:00.000 [C] P-RadioSpinel-: RCP => [C] Platform------: Extended Reset info: 0x401 (HV )
00:00:00.022 [C] Platform------: otPlatRadioSetMacKey() at radio.cpp:964: InvalidArgument
otbr-agent exited with code 2 (by signal 0).
Any ideas? This is running inside a VM.
Hi, I had the same problem a couple of days ago, apparently there is a bug in the 2026-08 release of OTBR that affects ZBT-2. I pulled and ran a older image and it worked without any problems.
Hi, I would just like to confirm that everything works for me and thanks to OP and everyone else on this forum who contributed. Here is my input.
I have a Raspberry Pi 3 B+ with official Raspberry Pi OS Lite.
I flashed my ZBT-2 via the home assistant actually (at first I thought the zbt-2 device goes into HA container).
Here are my 3 docker compose files:
Home Assistant:
services:
homeassistant:
container_name: homeassistant
image: ghcr.io/home-assistant/home-assistant:stable
# privileged: true
restart: unless-stopped
environment:
TZ: Europe/Ljubljana
volumes:
- ./config:/config
- /run/dbus:/run/dbus:ro #for bluetooth
network_mode: host
Matter server:
services:
matter-server:
image: ghcr.io/matter-js/python-matter-server:stable
container_name: matter-server
#restart: unless-stopped
network_mode: host
security_opt:
- apparmor=unconfined #perhaps needed for bluetooth device comissioning
volumes:
- ./data:/data
- /run/dbus:/run/dbus:ro #perhaps needed for bluetooth device comissioning
Thread router:
services:
otbr:
image: openthread/border-router:latest
container_name: thread-router
network_mode: host
cap_add: #allows container to change docker host's network settings, create a new interface...
- NET_ADMIN
# restart: unless-stopped
environment:
TZ: Europe/Ljubljana
OT_RCP_DEVICE: "spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=460800&uart-flow-control" #set proper settings for communication with ZBT-2
devices:
- /dev/serial/by-id/usb-Nabu_Casa_ZBT-2_DCB4D910DCE0-if00:/dev/ttyACM0 #first part is id of zbt-2 on docker host, second part is ACM0, which is default in this container. Must match OT_RCP_DEVICE
- /dev/net/tun:/dev/net/tun #tun is a virtual network interface
In addition to these compose files, the script vogon linked is also needed, or else device commissioning failed.
So the setup was overall quite easy and logical. Perhaps it helps someone or confirms to someone that it is possible on RPI 3.
In my setup HA is hosted on a server in a rack outside my main building, which is not an ideal location for the ZBT-2. Now an old RPi 3 is running just the thread container as border router in my living room. Works perfectly.