From c7151b7333877abf4997da4a6242a40a1313bdaa Mon Sep 17 00:00:00 2001 From: Oier Bravo Urtasun Date: Sun, 24 May 2026 06:03:26 +0200 Subject: [PATCH] fix(mdns): publish via host avahi over D-Bus (not a second daemon) A container running its own avahi-daemon collides with the host's on :5353. Instead publish to the host avahi over its mounted D-Bus socket using avahi-tools (avahi-publish). Needs apparmor=unconfined (docker-default blocks the D-Bus publish). Documented host prereqs incl. allow-interfaces for multi-bridge hosts. Tested: publish reaches host avahi and registers the names; on hosts with many docker bridges avahi self-collides until restricted to the LAN interface. Co-Authored-By: Claude Opus 4.7 (1M context) --- docker-compose.avahi.yml | 22 ++++++++++++++++++---- docker/avahi/Dockerfile | 9 +++++---- docker/avahi/entrypoint.sh | 24 ++++++++++++------------ 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/docker-compose.avahi.yml b/docker-compose.avahi.yml index 5875eb6..690f01e 100644 --- a/docker-compose.avahi.yml +++ b/docker-compose.avahi.yml @@ -1,7 +1,14 @@ -# mDNS toggle — publish *.local service names over Avahi for zero-config guest -# resolution. Enabled when ENABLE_MDNS=true in .env (build-stack layers it in) -# and only meaningful when BASE_DOMAIN=*.local. Host networking is required for -# link-local multicast. +# mDNS toggle — publish *.local service names via the HOST's avahi-daemon for +# zero-config guest resolution. Enabled when ENABLE_MDNS=true in .env and only +# meaningful when BASE_DOMAIN=*.local. +# +# Host prerequisites: +# - avahi-daemon running on the host +# - if the host has many interfaces (e.g. lots of docker bridges), restrict +# avahi to the LAN NIC in /etc/avahi/avahi-daemon.conf: +# allow-interfaces= +# otherwise avahi sees its own announcements across bridges → "Local name +# collision" and publishing fails. services: avahi: build: ./docker/avahi @@ -9,6 +16,13 @@ services: container_name: avahi restart: unless-stopped network_mode: host + # AppArmor's docker-default profile blocks the D-Bus publish to host avahi. + security_opt: + - apparmor=unconfined environment: BASE_DOMAIN: ${BASE_DOMAIN} HOST_LAN_IP: ${HOST_LAN_IP} + DBUS_SYSTEM_BUS_ADDRESS: "unix:path=/run/dbus/system_bus_socket" + volumes: + # Talk to the host's avahi-daemon (host must run avahi-daemon). + - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket diff --git a/docker/avahi/Dockerfile b/docker/avahi/Dockerfile index c51798d..f494f48 100644 --- a/docker/avahi/Dockerfile +++ b/docker/avahi/Dockerfile @@ -1,8 +1,9 @@ -# Self-contained mDNS responder: publishes the stack's service names on .local -# so guests resolve them with zero DNS/client config (macOS/Linux native; -# Windows needs Bonjour). Used only when ENABLE_MDNS=true and BASE_DOMAIN=*.local. +# Publishes the stack's service names on .local via the HOST's avahi-daemon +# (over its D-Bus socket — no second daemon, avoids the :5353 collision). +# Requires the host to run avahi-daemon and the socket to be mounted (see +# docker-compose.avahi.yml). Used when ENABLE_MDNS=true and BASE_DOMAIN=*.local. FROM alpine:3.20 -RUN apk add --no-cache avahi dbus +RUN apk add --no-cache avahi-tools COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/avahi/entrypoint.sh b/docker/avahi/entrypoint.sh index b7774aa..fd7f128 100755 --- a/docker/avahi/entrypoint.sh +++ b/docker/avahi/entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/sh -# Publish A records for the stack's service names over mDNS (.local). -# Requires host networking (mDNS is link-local multicast). One avahi-publish -# per name; if any dies the container exits so Docker restarts it. +# Publish A records for the stack's service names on .local via the HOST's +# avahi-daemon (reached over its mounted D-Bus socket). One avahi-publish per +# name; if any drops the container exits so Docker restarts it. set -eu : "${BASE_DOMAIN:?BASE_DOMAIN unset}" @@ -12,21 +12,21 @@ case "$BASE_DOMAIN" in *) echo "WARN: mDNS resolves only .local — BASE_DOMAIN=$BASE_DOMAIN will NOT be discoverable via avahi. Set BASE_DOMAIN=*.local to use mDNS." >&2 ;; esac -mkdir -p /run/dbus -rm -f /run/dbus/pid /run/dbus/system_bus_socket 2>/dev/null || true -dbus-daemon --system -for _ in 1 2 3 4 5; do [ -S /run/dbus/system_bus_socket ] && break; sleep 1; done - -avahi-daemon --daemonize --no-chroot -sleep 2 +bus="${DBUS_SYSTEM_BUS_ADDRESS:-unix:path=/run/dbus/system_bus_socket}" +sock=${bus#unix:path=} +if [ ! -S "$sock" ]; then + echo "ERROR: host D-Bus socket '$sock' not found. Mount the host's" >&2 + echo " /run/dbus/system_bus_socket and ensure avahi-daemon runs on the host." >&2 + exit 1 +fi +export DBUS_SYSTEM_BUS_ADDRESS="$bus" pids="" for n in "$BASE_DOMAIN" "auth.$BASE_DOMAIN" "packwiz.$BASE_DOMAIN" "mc.$BASE_DOMAIN"; do - echo "mDNS publish: $n -> $HOST_LAN_IP" + echo "mDNS publish (host avahi): $n -> $HOST_LAN_IP" avahi-publish -a "$n" "$HOST_LAN_IP" & pids="$pids $!" done -# Block on the publishers; exit (→ restart) if one drops. # shellcheck disable=SC2086 wait -n $pids 2>/dev/null || wait