This is Silabs' forked OpenThread Border Router (otbr) for multiprotocol support via CPCd in a container.
This version of otbr can connect via cpc instead of directly via the device interface, allowing the device to be used for other protocols via CPCd like Zigbee. It can be paired directly with a CPCd container as well.
The same environment vars used for the upstream otbr container are supported here too:
- OT_RCP_DEVICE: Specifies the connection to the Thread Radio Co-Processor (RCP).
- Default:
'spinel+cpc://cpcd_0?iid=2&iid-list=0'
- Default:
- OT_INFRA_IF: The network interface used for the adjacent infrastructure network (typically Wi-Fi or Ethernet).
- Default:
eth0
- Default:
- OT_THREAD_IF: The network interface name used for the Thread network.
- Default:
wpan0
- Default:
- OT_LOG_LEVEL: The verbosity level of logs generated by OpenThread.
- Default:
3
- Default:
You'll need to configure IP forwarding and RA. Refer to the OpenThread guide. The script may need to be modified if your backbone interface isn't wlan0.
sudo podman run --name=otbr -it --rm --privileged --network=host -v /run/dbus:/run/dbus:rw -v /dev/shm:/dev/shm:rw -v /etc/localtime:/etc/localtime:ro --device=/dev/net/tun ghcr.io/yichenshen/otbr-multiproto-container:latestNote that you need CPCd running when running with the default, which connects via CPC
Also note that otbr requires a fair bit of privileges (rootful + network=host) because it needs to create and manage the Thread interface wpan0
As of the 2025.12 Silabs SDK, Silab's OTBR no longer contains the REST API.
The default quadlet file can be installed to the root's systemd folder to run otbr with systemd. As per above, this has to be /etc/containers/systemd/ as the container needs to be rootful.
sudo cp otbr-multiproto.container /etc/containers/systemd/
sudo systemctl daemon-reload
sudo systemctl enable otbr-multiproto
sudo systemctl start otbr-multiprotoThe quadlet mounts 2 volumes, otbr-run and otbr-data. The former contains the otbr sockets, and can be mounted on another otbr-multiproto container to use the web GUI or ot-cli (see below) on the instance. The later stores Thread configuration data to be persisted between restarts.
otbr by default creates routes with metric 256. It is higher then other advertised routes say from Google's Nest Hub (105). As such, it doesn't route Thread addresses to itself but instead routes it to an external border router. It's advisable to adjust the metric so routing behaves as expected.
For example if your Thread IP prefix is fdae:3c84:2ad6:1:
sudo ip -6 route replace fdae:3c84:2ad6:1::/64 dev wpan0 metric 105 pref mediumThe quadlet container will do this after start automatically, though you'll need to change your Off-Mesh-Routable address (fdae:3c84:2ad6:1) to your own.
OMR address, Off-Mesh-Routable address, is the address the you use to route from outside the thread network to nodes inside. Each border router has their local OMR. With multi routers, the Thread leader will pick a OMR prefix to use for the whole network. This means that if the border router that owns the OMR goes offline, the prefix of all the nodes could switch to the secondary OMR prefix. That'll mess up your firewall settings.
One easy way to avoid this is to configure your border routers to have the same OMR with ot-ctl.
If you're using firewalld and have multiple border routers that you intend to join in one network, you need to turn off RP filter in /etc/firewalld/firewalld.conf.
IPv6_rpfilter=no
RP filter essentially blocks traffic if the source IP isn't routed to the same interface it can from. However, with more than one router you're essentially multi-homed and this will cause intentionally rejects.
You probably also want to allowlist mdns multicast:
sudo firewall-cmd --zone=<zone> --add-rich-rule='rule family="ipv6" destination address="ff02::/16" accept'
Lastly, if your infra interface (e.g. eth0 or wlan0) is not in the same zone as your thread interface (e.g. wlan0), then you'll want to allow forwarding between the zones.
Say is eth0 is on zone home and otbr's wpan0 is on zone thread.
sudo firewall-cmd --permanent --new-policy=server-to-wpan
sudo firewall-cmd --permanent --policy=server-to-wpan --set-target=ACCEPT
sudo firewall-cmd --permanent --policy=server-to-wpan --add-ingress-zone=home
sudo firewall-cmd --permanent --policy=server-to-wpan --add-egress-zone=thread
sudo firewall-cmd --permanent --new-policy=wpan-to-server
sudo firewall-cmd --permanent --policy=wpan-to-server --set-target=ACCEPT
sudo firewall-cmd --permanent --policy=wpan-to-server --add-ingress-zone=thread
sudo firewall-cmd --permanent --policy=wpan-to-server --add-egress-zone=home
sudo firewall-cmd --reload
Both the web GUI anf ot-cli are installed on the same container, but they're not running by default. The quadlet provided stores the otbr socket on a named volume, which we can then mount onto another container to run these tools.
sudo podman run --name=otbr-web --rm -it --network=host -v otbr-run:/run:rw -v /etc/localtime:/etc/localtime:ro ghcr.io/yichenshen/otbr-multiproto-container:latest otbr-web -p 8080
sudo podman run --name=ot-ctl --rm -it --network=host -v otbr-run:/run:rw -v /etc/localtime:/etc/localtime:ro ghcr.io/yichenshen/otbr-multiproto-container ot-ctl
The first command runs the web GUI on port 8080. The second lands you in the OpenThread command line.
Note that because the REST API is not included, the topology view will not work.
You can join/create network using the web GUI, or with ot-ctl. Refer to the OpenThread Guide.
With Home Assistant, you want to look for the TLV dataset in /config/.storage/thread.datasets after sharing the auth credentials to Home Assistant. Use ot-ctl to set the dataset onto OTBR to have it join the network.
See the LICENSE file for license rights and limitations (MIT).