How mDNS works in 10 lines
- Each host publishes its hostname plus its IP via UDP multicast to
224.0.0.251port 5353. - When a query for
foo.localcomes in, every host on the segment sees it; the host whose hostname matches responds with its IP. - No central server. No DNS configuration. Works within one L2 broadcast domain.
- Companion protocol DNS-SD (DNS Service Discovery) lets hosts advertise services: "I'm offering SSH on port 22," "I'm an HTTP server on port 80," "I'm a printer."
Install Avahi on Debian / Ubuntu
sudo apt install avahi-daemon avahi-utils libnss-mdns
sudo systemctl enable --now avahi-daemon
sudo systemctl status avahi-daemon
libnss-mdns is critical — it's what hooks mDNS into the system resolver. Without it, applications calling getaddrinfo for .local names won't find anything; only avahi-utils CLI would.
Set the hostname
# Persistent across reboots
sudo hostnamectl set-hostname my-server
echo my-server | sudo tee /etc/hostname
# Avahi picks it up automatically; verify
avahi-resolve --name my-server.local
# my-server.local 192.168.1.42
From any other Avahi-enabled host (or macOS / iOS / Android) on the same LAN: ping my-server.local just works.
What works automatically
<hostname>.localresolves to that host's IP.- SSH:
ssh my-server.local - HTTP: browse to
http://my-server.local:8080 - Files:
scp file.txt my-server.local:/tmp/
Same for any other Avahi/Bonjour-aware host; macOS and Windows (with Bonjour Print Services or modern Windows 10/11 native mDNS support) work transparently with Linux Avahi hosts.
Advertise services via DNS-SD
Drop a service-definition XML in /etc/avahi/services/:
<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name replace-wildcards="yes">%h HTTP server</name>
<service>
<type>_http._tcp</type>
<port>80</port>
<txt-record>path=/</txt-record>
</service>
</service-group>
%h is replaced with the hostname at runtime. After saving, reload Avahi:
sudo systemctl restart avahi-daemon
Other hosts can now find your HTTP service:
avahi-browse -art
# Browses all services; shows your HTTP entry with type _http._tcp, port 80
# Programmatic
avahi-browse -art --resolve --no-db-lookup _http._tcp
Common service types
_ssh._tcp— SSH server_sftp-ssh._tcp— SFTP_http._tcp/_https._tcp— HTTP/S_smb._tcp— Samba shares (auto-discovered by macOS Finder)_workstation._tcp— generic "workstation"; shows up in Finder's Network_ipp._tcp— CUPS printer_airplay._tcp,_raop._tcp— AirPlay / Bonjour audio
For one-off service advertising without writing XML:
avahi-publish -s "My Web" _http._tcp 8080
What doesn't cross subnets
mDNS is link-local: it doesn't cross routers. A device on VLAN A can't see mDNS announcements from VLAN B. Two ways around this:
- Avahi reflector mode — one machine on the boundary forwards multicast between interfaces. Add to
/etc/avahi/avahi-daemon.conf:
Useful for letting a "trusted" VLAN see IoT-VLAN devices.[reflector] enable-reflector=yes - mdns-repeater / avahi-daemon on the router itself — OPNsense (see that tutorial) has an mDNS Repeater plugin; OpenWrt has
avahi-daemonas an opkg.
For most home setups with one flat LAN, this isn't relevant.
The .local TLD warning
For historical reasons, some Active Directory deployments used .local as their internal DNS suffix. On a network with both AD .local and Avahi .local, the two collide — queries can go to the wrong system. Microsoft now recommends against using .local for AD precisely because of this conflict.
If you're on an AD-controlled corporate network, mDNS / Avahi may be blocked or unreliable. For your homelab, it's perfect; for corporate-network use, check with IT first.
Disable on production servers facing the internet
Avahi multicasting on a public-facing interface is mostly harmless but adds noise. On a cloud VPS:
sudo systemctl disable --now avahi-daemon
You won't miss anything — a VPS doesn't share an L2 with anything that cares about mDNS. Save Avahi for LAN-attached machines.
For server-to-server discovery beyond mDNS
- Tailscale's MagicDNS (see that tutorial) gives you
hostname.ts.netresolution across machines, regardless of L2 boundaries. - Consul has a built-in DNS interface for service discovery.
- NetBox (see that tutorial) + a small generator script can produce a real DNS zone for your homelab.
For "I want to ssh to laptop.local without any setup," mDNS is the right tool. For everything beyond one LAN, graduate to one of the above.