Matter Server Docker Container on Synology NAS / Home Assistant Core

Hey guys,

I have not found any good tutorial to install the home assistant matter-server (GitHub - home-assistant-libs/python-matter-server: Python server to interact with Matter) docker container on a Synology NAS (in my case a DS416play). So I wanted to share my approach:

In my docker folder a created the new folder (matter-server + data) like:
/volume1/docker/matter-server/data

Within DSM I activated IPv6 support in the network options:
System Settings/Network/Network Interface → Edit you used network connection → IPv6 Tab → IPv6 Setup: Automatic

With portainer I deployed then the following stack:

version: '3'
services:
  matter-server:
    container_name: matter-server
    image: ghcr.io/home-assistant-libs/python-matter-server:stable
    restart: unless-stopped
    security_opt:
      - apparmor=unconfined
    volumes:
      - /volume1/docker/matter-server/data:/data:rw
      - /run/dbus:/run/dbus:ro
    network_mode: host

With this and my SynologyIP address I would add the matter integration with:
ws://:5580/ws

Adding the Devices was possible with the iOS companion app as described here:

Maybe it will help someone :slight_smile:

6 Likes

Screenshot 2024-08-12 152620
Does it still say IPv6 disabled on the docker network host for you? I feel this is the reason even though Matter server is running for me, I can’t add devices successfully via Thread even with a valid Thread boarder router (Apple TV 4K) running

Sorry I have not seen your replay … according to the Container Manager of Synology there is nothing set for IP6 for me :confused:
So I guess ist also disabled?

So are you able to start your container @Nevs88 without the following error I have instead?

e[32m2024-11-10 08:26:48.756e[0m (Dummy-2) e[1;30mCHIP_ERRORe[0m e[34m[chip.native.DIS]e[0m e[31mFailed to advertise records: src/inet/UDPEndPointImplSockets.cpp:416: OS Error 0x02000065: Network is unreachablee[0m

No actually I have the same error message in the docker logs … what does it mean?
(I use a SwitchBot Hub 2 over matter … humidity and temperature are well displayed)

Hey guys, same issue, container ok, ipv6 ok, integration added in home assistant. I try to add a new device with home assistant companion, it connects to the device but it sticks to configuration step :confused:
always the same thing in the log :

|2025/01/17 22:59:55|stderr|e[32m2025-01-17 21:59:55.379e[0m (Dummy-2) e[1;30mCHIP_ERRORe[0m e[34m[chip.native.SC]e[0m e[31mPASESession timed out while waiting for a response from the peer. Expected message type was 33e[0m|
|2025/01/17 22:59:43|stderr|e[32m2025-01-17 21:59:43.551e[0m (Dummy-2) e[1;30mCHIP_ERRORe[0m e[34m[chip.native.EM]e[0m e[31m<<5 [E:57549i with Node: <0000000000000000, 0> S:0 M:143609138] (U) Msg Retransmission to 0:0000000000000000 failure (max retries:4)e[0m|

I don’t know what to do, if somebody has an idea :slight_smile:

Hey,
I am currently having the same issue as FredGH37, however I got it working before so its definitly possible but I am currently unable to find out were the solution is.
But here is my setup an my assumtion.
I am running my IOT on VLAN2 (acutally I dont think it matters as long as you are on the same VLAN with the whole stack).
I have a portainer installed on my synology (in its own network with own ipv6 subnet, but I dont think this is the problem).
Portainer is running hass and python-matter-server (and some more stuff like mqtt) all on host network (all ip address are correctly assigned as far as I see).
I am using a Unifi network with a USG firewall and here is my assumtion I tried to play around a lot with the firewall rules before switching to VLAN2 and at some point I got it working but now I am stuck again.
I have tried opening ports 5039 & 5040, I have explicitly allowed access to mDNS ips like 224.0.0.251, I have set up IMCP echo reply and some other stuff and at some point it worked but I cannot say which rule fixed it and I am currently unable to get my setup to work again.
But I am pretty sure it is possibly

I had the same issue as FredGH37 and it was caused by improper OS configuration.

I think that the problem was the box running Matter controller did not have a ipv6 prefix in the routing table (which is supposed to be added automatically by RA) so the Matter controller could not send packets to the device being commissioned.

Please make sure you read the official OS requirements info thoroughly. In my case, as I am using Gentoo with OpenRC (no NetworkManager, no systemd-networkd), I had to compile the kernel with CONFIG_IPV6_ROUTE_INFO and set the two ra-related sysctls methioned there. In my case, because the box is used as the main router and I wanted ipv6 routing, I had to set 2 to net.ipv6.conf.br0.accept_ra where br0 is the bridge with the network where my border router reside and ensure nothing will mess up with that sysctl like dhcpcd mentioned in the linked article. I was running Home Assistant Matter controller docker container on the same host with network_mode=host (the container can be run as a non-root user if you want - tested).

What also worked for me was moving the matter controller container to a different box in the same network, that box was connected to a switch to which the Thread border router was connected as well and it was running the latest x86_64 Arch Linux with systemd-networkd - surprisingly no kernel/routing configuration was necessary in that case. But after fixing the mentioned OS config I ended up with having everything in that gentoo box (just the Thread border router external, at the same network place). So it seems to really be related to the proper ipv6 and RA config. Also mDNS multicast traffic must work in the network (not be blocked by a firewall). And the Thread border router and the controller has to be on the same (v)LAN network. Hope it helped to direct you to the right path.

Hi,

For what it is worth, have not had much time to play. Had the same issue mentioned here. additionally after the 4 fails it the following errors reported mDNS resolves did not work.

Setup:

  • synology
  • dockerized: HA, zigbee2mqtt, ebus, mousquito, matter-server

I started with all in a bridged network exposing ports, except the matter-server network set to host.
I got the same error followed by mDNS error. Don’t have the exact line anymore.

My network is fully ipv6: with each device having global, ula and link local ipv6 set. Dhcpv6, etc. My Synology has ipv6 enable and an ULA, global and link local address. Pretty sure a global is not required, but hey, since I was setting this up.

I moved all above ha related containers to a mcvlan and assigned an ipv4

networks:
  #Creating the network
  nw-mcvlan-main:
    #external: true
    name: "nw-mcvlan-main"
    enable_ipv6: true
    driver: macvlan
    driver_opts:
      parent: ovs_eth4
    ipam:
      driver: default
      config:
        - subnet: 192.168.1.0/24
          gateway: 192.168.1.1
          ip_range: 192.168.1.192/29
        - subnet: fd86:xxxx:adce:0:0:0:0:0/64
          gateway: fd86:xxxx:adce:0:de39:6fff:fe0f:57b3

assigned each container an ipv4. Example:

    ha3-Zigbee2MQTT:
        container_name: "ha3-Zigbee2MQTT"
        hostname: "ha3-Zigbee2MQTT"
        image: "koenkk/zigbee2mqtt:latest"
        ...
        networks:
            nw-mcvlan-main:
                ipv4_address: 192.168.1.194
        #ports:
            #- "8124:8080/tcp"

For matter-server: remove the “networkmode: host” and add similar, each with it’s own ip that fits the iprange (mine is 192.168.1.193-197) Please adjust ranges to your network setup.

Reconfigured the connections between service to match the new ip’s, and I was able to join a matter over wifi device successfully.

Need to spend more time, reading up and trying some other things, next will be getting an matter over thread device and configure that one. Also figure out why mcvlan would work and and host+bridge does not work.
Would HA and matter-server be required to be in the same network somehow?

Anyway, just posting my findings, I hope I find some time to get deeper into it. If not, it might help others. Or not.

I use MacVLAN, but it still does not work on my Synology (CONFIG_IPV6_ROUTE_INFO not enabled in the kernel).
I get the same error messages from matter-server both when it uses macvlan and when it uses network_mode: host:

(MainThread) INFO [matter_server.server.server] Matter Server successfully initialized.
(Dummy-2) CHIP_ERROR [chip.native.DIS] Failed to reply to query: src/inet/UDPEndPointImplSockets.cpp:421: OS Error 0x02000065: Network is unreachable
(MainThread) INFO [matter_server.server.device_controller] Starting Matter commissioning with code using Node ID 7.
(Dummy-2) CHIP_ERROR [chip.native.CTL] Discovery timed out
(Dummy-2) CHIP_ERROR [chip.native.EM] <<5 [E:31455i with Node: <0000000000000000, 0> S:0 M:194939517] (U) Msg Retransmission to 0:0000000000000000 failure (max retries:4)
(Dummy-2) CHIP_ERROR [chip.native.SC] PASESession timed out while waiting for a response from the peer. Expected message type was 33

This is my compose.yaml:

networks:
  macvlan1:
    name: macvlan1
    driver: macvlan
    driver_opts:
      parent: ovs_bond0
    enable_ipv6: true
    ipam:
      config:
        - subnet: 10.4.20.0/23
          ip_range: 10.4.21.0/24
          gateway: 10.4.20.1

services:
  matter-server:
    container_name: matter-server
    hostname: matter-server
    image: "ghcr.io/home-assistant-libs/python-matter-server:stable"
    mac_address: "0e:be:00:da:00:66"
    networks:
      macvlan1:
        ipv4_address: 10.4.21.66
    security_opt:
      - "apparmor:unconfined"
    sysctls:
      net.ipv6.conf.eth0.accept_ra: 2
      net.ipv6.conf.eth0.forwarding: 1
    volumes:
      - "/volume1/docker/hass/matter-server:/data"
      - "/run/dbus:/run/dbus:ro"

  hass:
    container_name: hass
    hostname: hass
    image: "ghcr.io/home-assistant/home-assistant:stable"
    mac_address: "0e:be:00:da:00:70"
    networks:
      macvlan1:
        ipv4_address: 10.4.21.70
    privileged: true
    volumes:
      - "/volume1/docker/hass/config:/config"
      - "/etc/localtime:/etc/localtime:ro"
      - "/run/dbus:/run/dbus:ro"

I had to add a route inside the matter-server container, and now everything seems to work:

ip -6 route add <1> via <2> dev eth0 src <3>

Where <1> is the unique local address range used by my Thread devices (fd49:2203:740f::/64), <2> is the link-local IPv6 of my Thread Border Router (fe80:: address of my Apple TV), and <3> is the IPv6 of the container (2001: address of matter-server).

I got <1> and <2> by running

netstat -rn -f inet6 | grep UGc | grep -v default

on a device that correctly accepts routes (as opposed to Synology), and <3> from inside the container using ip addr | grep dynamic.

Note that you have to add

    cap_add:
      - NET_ADMIN

to the matter-server service in the Docker Compose file to be able to add routes using ip.

Now only to make this persistent and not sensitive to IP changes… :smiley:

This is the script I use now on my Synology NAS:

#!/bin/sh

# Define the Unique Local Address (ULA) prefix
ULA_PREFIX="fd49:2203:740f::/64"
PREFIX_PART="${address%%::*}:"

# Fetch the Matter devices' IPv6 addresses
echo "Fetching the Matter devices' IPv6 addresses..."
ULA_ADDRESSES=$(avahi-browse -rpt _matter._tcp | grep -E "=;.*${PREFIX_PART}" | awk -F ";" '{print $8}' | sort -u)

# Fetch the Thread Border Router's link-local address
echo "Fetching the Thread Border Router's link-local address..."
THREAD_BR=$(rdisc6 ovs_bond0 | grep -A4 "$ULA_PREFIX" | grep "from" | head -1 | awk '{print $2}')

# Check if rdisc6 failed to fetch the address
if [ -z "$THREAD_BR" ]; then
    # Fallback to using curl to fetch the Thread Border Router's IPv6 address
    echo "rdisc6 failed to fetch Thread Border Router's IPv6 address, attempting fallback with curl..."
    THREAD_BR=$(curl -k -X GET 'https://<UniFi Cloud Gateway's address>/proxy/network/api/s/default/stat/sta' -H 'X-API-KEY: <my API key>' -H 'Accept: application/json' | jq -r '.data[] | select(.mac == "<Apple TV's MAC address>") | .ipv6_addresses | if type == "array" then . | map(select(startswith("fe80::"))) | .[0] else select(startswith("fe80::")) end')
    # Ensure THREAD_BR is not empty after fallback
    if [ -z "$THREAD_BR" ]; then
        echo "Error: Unable to fetch Thread Border Router's IPv6 address."
        exit 1
    fi
fi

# Get the dynamic IPv6 address of eth0 inside the Docker container
echo "Getting the dynamic IPv6 address of eth0 inside the Docker container..."
DYNAMIC_IPV6=$(docker exec matter-server ip -6 addr show eth0 | awk '/scope global dynamic/{print $2}' | cut -d/ -f1)

# Ensure DYNAMIC_IPV6 is not empty
if [ -z "$DYNAMIC_IPV6" ]; then
    echo "Error: Unable to fetch dynamic IPv6 address from container."
    exit 1
fi

# Run the route management commands inside the Docker container
docker exec matter-server sh -c "
    ULA_PREFIX=\"$ULA_PREFIX\"
    THREAD_BR=\"$THREAD_BR\"
    DYNAMIC_IPV6=\"$DYNAMIC_IPV6\"
    ULA_ADDRESSES=\"$ULA_ADDRESSES\"

    echo \"Checking if a route to \${ULA_PREFIX} already exists...\"
    if ip -6 route show | grep -q \"\$ULA_PREFIX\"; then
        echo \"Removing old route...\"
        ip -6 route del \"\$ULA_PREFIX\"
    fi

    echo \"Adding the new route via \${THREAD_BR} with source address \${DYNAMIC_IPV6}...\"
    if [ -n \"\$DYNAMIC_IPV6\" ]; then
        ip -6 route add \"\$ULA_PREFIX\" via \"\$THREAD_BR\" dev eth0 src \"\$DYNAMIC_IPV6\"
    fi

    echo \"Checking connectivity...\"
    for ip in \${ULA_ADDRESSES}; do
        if ping6 -q -c 1 \$ip >/dev/null 2>&1; then
            echo \"\$ip is reachable\"
        else
            echo \"\$ip is not reachable\"
        fi
    done
"

1 Like

The main problem from running the matter server in host or macvlan mode on a Synology NAS is that the linux kernal they’re using it missing CONFIG_IPV6_ROUTE_INFO.

This keeps the router advertisements from the thread border routers from being parsed and put into the routing table.

I’m working on a script similar to @ebertek that will listen for these router advertisements and place them into the routing table (and remove them after expiring).

Trying to get this script workable inside the container using the post_start: hook in docker compose so that it’ll be a single workaround for both macvlan and host networking

Thank you @ebertek for the initial start

Here’s mine; simpler because it just assumes the last announcement TBR will work. And I run the container in host mode, so no need to run commands inside docker.

#!/bin/sh
set -e

IFACE="eth0"
RDISC_OUTPUT=$(rdisc6 "$IFACE")

echo "🔍 Extracting ULA routes..."

echo "$RDISC_OUTPUT" | awk '
  BEGIN { prefix = ""; from = "" }
  /^ Prefix|^ Route/ && $3 ~ /^fd/ { route = $3 }
  /^ from / { from = $2; print route, from; route = "" }
' | sort -u | while read -r prefix router; do
  [ -z "$prefix" ] && continue
  [ -z "$router" ] && continue

  echo "📡 ULA/Route: $prefix via $router"

  ip -6 route show | grep -q "$prefix" && {
    echo "🧹 Removing existing route to $prefix"
    ip -6 route del "$prefix" 2>/dev/null || true
  }

  echo "➕ Adding route to $prefix via $router on $IFACE"
  ip -6 route add "$prefix" via "$router" dev "$IFACE" && echo "✅ Added"
done
1 Like