DNS-SD Service Discovery on a Local Network: mDNS Behavior Under Load
mDNS works well until it doesn't. On a network with 80+ IoT devices all announcing simultaneously after a power cycle, the probing storm is a real problem.

mDNS (RFC 6762) and DNS-SD work until eighty IoT devices power-cycle simultaneously and flood the LAN with probing packets. Our smart-building pilot—802.11ax AP, /24 subnet, Avahi 0.8 on gateways, custom Zephyr mDNS on sensors—saw 90-second discovery blackouts after building-wide UPS test. This note documents packet-level behavior and mitigations that actually helped.
Baseline behavior (small network)
- Service type:
_iot-sensor._udp.local - Hostname pattern:
sensor-{mac-last-3}.local - Probe sequence: 3 queries spaced ~250 ms with exponential backoff on conflict
- Announce after successful probe
Works fine at 10 devices. At 80+, probabilistic delay (RFC recommended) was not implemented on our firmware—every device probed immediately on link-up.
What we saw on Wireshark
After controlled power restore:
- T+0–2 s: ~2400 mDNS packets/s on VLAN 40 (IoT)
- T+2–30 s: sustained 800 pkt/s—mostly
QMquestions for conflicting names - T+30–90 s: duplicate hostname collisions; 12 sensors renamed to
sensor-abc-2.localby conflict resolution—mobile app cache stale
CPU on Pi gateway (Avahi reflector between Wi-Fi and Ethernet) pegged one core. User symptom: "app shows offline devices" though ping worked.
Root causes
- Synchronized boot — PoE switch energizes all ports together
- Identical probe timing — no random startup jitter in firmware
- Aggressive cache flush on app — iOS mDNS cache TTL 120 s; conflicts during probe storm served stale SRV records
- Reflector amplification — Avahi reflecting every query across interfaces doubled airtime on Wi-Fi
Mitigations (ranked by impact)
Staggered boot (hardware/software)
// Zephyr net_if_up callback — do not probe immediately
uint32_t jitter_ms = (sys_rand32_get() % 30000); /* 0–30 s */
k_work_schedule(&mdns_start_work, K_MSEC(jitter_ms));
Cut peak pkt/s from 2400 to 180. Building UX: devices appear gradually—acceptable for sensors, not for latency-critical alarms without parallel static fallback.
Unicast mDNS where possible (RFC 6762 hybrid)
Gateway maintains authoritative registry; sensors register via CoAP to gateway; gateway answers mDNS for _iot-sensor._udp.local on behalf of subnet. Reduced contending probes; added gateway SPOF—documented.
Dedicated IoT VLAN + mDNS boundaries
Stop reflecting IoT mDNS to corporate VLAN. Apple Bonjour gateway at 35 devices max per AP radio per Apple guidance—we enforced AP client limit 40 on IoT SSID.
Static DNS-SD fallback config
JSON blob in app: { "gateway": "iot-gw.local", "fallback_ip": "10.40.1.1" } when mDNS fails 30 s. Unromantic but stopped support calls.
Cross-reference MQTT vs CoAP for uplink after discovery; network namespace routing debugging when reflector rules get weird in containers.
Load test setup
- 80× ESP32-C3 dev boards flashed with probe-storm firmware branch
- Netgear M4300 switch, controlled PDUs
- Capture on mirror port to laptop (tcpdump 4.99)
What I'd do next
- Implement RFC 6762 conflict defense completely—tie-breaking on name, not random reboot loops.
- Cap probes per second firmware-wide global rate limiter on UDP 5353 TX.
- Monitor mDNS pkt rate as SNMP metric on IoT APs—alert before user-visible failure.
Apple vs Android mDNS cache
iOS caches SRV records aggressively; Android Network Service Discovery API refreshed faster in our tests. Hybrid app used Bonjour fallback only on iOS with explicit NWBrowser restart on network change—Android used direct IP after first successful resolve stored in encrypted prefs.
Multicast rate limiting on AP
Enterprise AP firmware (Aruba) exposed mdns-proxy max-queries-per-sta—tuning to 15/s per client reduced storm collateral without blocking legitimate probes. Consumer APs lacked knob; VLAN segmentation only option.
Multicast vs unicast query load
After staggered boot, unicast responses from gateway cut multicast RX load on Wi-Fi clients 60% in week-long soak—phones stopped heating on IoT VLAN test. Tradeoff: gateway becomes discovery SPOF; we run primary + standby gateway with VRRP virtual IP for mDNS proxy.
RFC 6762 name conflict case study
Two sensors with same hostname after MAC clone mistake during RMA—both probed indefinitely, neither announced. Manufacturing now burns unique hostname from flash UID into OTP block; RMA reflashes full identity, not MAC override alone.
IPv6 multicast scope
mDNS on IPv6 link-local added duplicate probe traffic during dual-stack rollout—disabled IPv6 on IoT VLAN until devices supported v6 mDNS correctly. Half-migrated stacks doubled airtime waste.
DNS-SD TXT record size
Oversized TXT records (>130 B after fragmentation) caused some clients to ignore service entirely—trimmed _iot-sensor._udp TXT to version + capabilities hash only; full config fetched via HTTPS after resolve.
VLAN mDNS gateway RFC compliance
Proxy must not rewrite PTR records in ways that violate RFC 6762 conflict rules—we broke HomeKit coexistence until Avahi config aligned with Apple's mDNS proxy expectations on shared VLAN.
mDNS is fine for demos. Production fleets need jitter, boundaries, and a non-mDNS escape hatch—assume the probe storm will happen on first real power event.
Manish Bookreader
Electronics enthusiast, Embedded Systems Expert, Linux/Networking programmer, and Software Engineer passionate about AI, electronics, books, and cooking.

