feat(mdns): optional Avahi .local responder (ENABLE_MDNS)

Self-contained host-network Avahi container publishes the service names over
mDNS so .local works zero-config for guests (macOS/Linux native, Windows needs
Bonjour). Toggle with ENABLE_MDNS=true + BASE_DOMAIN=*.local; build-stack layers
docker-compose.avahi.yml in and builds the image during --prep. mDNS can't serve
the air-gap upstream spoofs (non-.local) — those still need real DNS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-24 05:56:54 +02:00
parent f14578dc65
commit 8eeda1265c
3 changed files with 54 additions and 0 deletions

32
docker/avahi/entrypoint.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/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.
set -eu
: "${BASE_DOMAIN:?BASE_DOMAIN unset}"
: "${HOST_LAN_IP:?HOST_LAN_IP unset}"
case "$BASE_DOMAIN" in
*.local) ;;
*) 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
pids=""
for n in "$BASE_DOMAIN" "auth.$BASE_DOMAIN" "packwiz.$BASE_DOMAIN" "mc.$BASE_DOMAIN"; do
echo "mDNS publish: $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