mDNS and Avahi: Local Service Discovery That Works Until It Doesn't

How .local names resolve themselves, and the day they suddenly stop

You have done this a hundred times without thinking about it. You plug a Raspberry Pi into the network, type ssh [email protected], and it just works — no DNS server, no editing /etc/hosts, no looking up an IP that DHCP hands out semi-randomly. That .local resolution is multicast DNS (mDNS), and on Linux the daemon doing the heavy lifting is almost always Avahi. It is genuinely brilliant when it works, and genuinely maddening on the days it doesn’t, mostly because nobody ever explains what it’s actually doing.

Advertisement

Ordinary DNS is a hierarchy of servers you query by unicast. mDNS throws that out. There is no server. When your laptop wants to find raspberrypi.local, it sends a query to the multicast group 224.0.0.251 on UDP port 5353, and every device on the segment hears it. The machine that owns that name answers, also by multicast, and now everyone has cached the result. It’s gossip, not a registry. The whole .local top-level domain is reserved by RFC 6762 for exactly this, which is why you must never use .local for real internal DNS — the two systems will fight.

Layered on top is DNS-SD (service discovery), the protocol behind Apple’s Bonjour. It’s how AirPlay receivers, printers, and Chromecasts announce themselves. A device doesn’t just claim a name; it advertises services — “I am a _ipp._tcp printer at this host, port 631, here’s a TXT record with my capabilities.” Your OS browses for those service types and populates the printer dialog without you typing anything.

On Debian or Ubuntu, avahi-daemon is usually already running. The tools that ship with it are the fastest way to see whether the magic is happening:

$ avahi-browse -art
+   eth0 IPv4 office-printer       _ipp._tcp    local
+   eth0 IPv4 living-room-tv       _googlecast._tcp  local
=   eth0 IPv4 office-printer       _ipp._tcp    local
   hostname = [office-printer.local]
   address = [10.0.0.42]
   port = [631]
   txt = ["rp=ipp/print" "ty=HP LaserJet"]

-a browses all service types, -r resolves each to a host and address, -t terminates after the initial sweep instead of watching live. To resolve a single name without a full DNS stack:

$ avahi-resolve -n raspberrypi.local
raspberrypi.local   10.0.0.51

Publishing your own service is a one-file affair. Drop this into /etc/avahi/services/ssh.service and Avahi advertises your SSH daemon to the whole segment:

<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
  <name replace-wildcards="yes">%h SSH</name>
  <service>
    <type>_ssh._tcp</type>
    <port>22</port>
  </service>
</service-group>

%h expands to the hostname. No restart needed — Avahi watches that directory and picks up the file within seconds.

Here is where the romance ends. mDNS relies on multicast reaching every device, and a depressing number of things quietly break that assumption.

Subnets. Multicast does not cross routers. The moment your IoT VLAN and your laptop VLAN are separate networks, .local discovery stops dead between them — by design. This is the single most common cause of “my Chromecast vanished after I segmented the network.” The fix is an mDNS reflector. Avahi can do it itself; edit /etc/avahi/avahi-daemon.conf:

[reflector]
enable-reflector=yes
reflect-ipv=no

Restart the daemon and Avahi relays mDNS packets between the interfaces it’s bound to, stitching your VLANs back together for discovery purposes. (Many home routers and OpenWrt call this same feature “mDNS repeater” or “Bonjour reflection.”)

WiFi power saving and IGMP snooping. Cheap switches with IGMP snooping enabled but no querier configured will drop multicast on the floor. Aggressive WiFi power-save modes let clients miss the multicast advertisements while dozing. Both produce the maddening symptom where a device is reachable by IP but its .local name resolves intermittently or not at all.

The resolver order. On systemd hosts, name resolution goes through nsswitch.conf. If mdns4_minimal isn’t in the hosts: line, getent hosts raspberrypi.local fails even with Avahi running fine:

$ grep ^hosts /etc/nsswitch.conf
hosts: files mdns4_minimal [NOTFOUND=return] dns

Avahi can be answering on the wire perfectly while glibc never even asks it. avahi-resolve talks to the daemon directly and will work regardless, which is a useful way to bisect the problem: if avahi-resolve succeeds but ssh host.local fails, your bug is in nsswitch, not the network.

For a single flat home network, mDNS via Avahi is a quiet miracle — zero configuration, names that follow machines around even when DHCP shuffles their addresses, printers and TVs that appear by themselves. Lean on it freely. The instant you introduce VLANs, mesh WiFi, or managed switches, treat it as a fragile convenience rather than infrastructure: enable the reflector, fix your IGMP querier, and keep a real DNS entry as a fallback for anything you actually depend on. It works until it doesn’t, and now you know which knob to reach for when it doesn’t.

Advertisement

Related Content

Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.