Raspberry Pi · Networking

LAN Service Discovery

How hydroMazing, NEXUS, rpi-sync, and other local services discover and communicate over your LAN — without cloud intermediaries, without static IPs, without configuration headaches.

⏱ 18 min 📊 Beginner+ 📅 Updated April 2026

What You'll Build

A zero-configuration LAN where Raspberry Pi nodes automatically discover each other by service name — no IP addresses to remember, no DNS server to maintain, no cloud dependency. Each Pi announces what it does; other Pis find it by asking "who can sync files?" or "who monitors systems?"

The Problem

Traditional networked services rely on one of three approaches, all problematic for self-hosted Pi clusters:

  • Static IPs — Assign each Pi a fixed address. Breaks when DHCP leases expire or you add new nodes.
  • Central DNS — Run a local DNS server. Adds complexity and a single point of failure.
  • Cloud registry — Use a SaaS discovery service. Defeats the purpose of self-hosting.

mDNS (multicast DNS) offers a fourth way: decentralized, zero-config, completely local.

How mDNS Works

mDNS lets devices resolve hostnames to IP addresses without a central DNS server. When a Pi joins the network, it announces itself. When another Pi wants to connect, it asks the network "who is pi-workshop.local?" and gets the current IP.

Pi joins LAN
Announces hostname.local
Other Pis cache response
SSH to pi.local works

Phase 1: Install Avahi (mDNS)

Avahi is the Linux implementation of mDNS/DNS-SD. Install it on every Pi in your network.

Install Avahi on Raspberry Pi
sudo apt update && sudo apt install -y avahi-daemon avahi-utils
Reading package lists... Done
Building dependency tree... Done
✓ Avahi installed
sudo systemctl enable --now avahi-daemon
Created symlink /etc/systemd/system/dbus... → avahi-daemon

Configure Hostname

Set a memorable hostname on each Pi. This becomes hostname.local.

Set Hostnames
# On your main development Pi
sudo hostnamectl set-hostname pi-workshop
# On your garden controller Pi
sudo hostnamectl set-hostname pi-garden
# On your AI/ML Pi
sudo hostnamectl set-hostname pi-nexus
sudo reboot

Test mDNS Resolution

After reboot, verify mDNS works between nodes:

Test mDNS
# From pi-workshop, ping pi-garden
ping pi-garden.local
PING pi-garden.local (192.168.1.102) 56(84) bytes of data.
64 bytes from pi-garden.local (192.168.1.102): icmp_seq=1 ttl=64 time=0.5 ms
✓ mDNS resolution working
# Browse all services on the LAN
avahi-browse -a
+ eth0 IPv4 pi-workshop [00:1a:2b:3c:4d:5e] Workstation
+ eth0 IPv4 pi-garden [aa:bb:cc:dd:ee:ff] Workstation

Phase 2: Service Discovery (DNS-SD)

Beyond hostnames, we want to discover services — "who can sync files?" "who monitors systems?" DNS-SD (DNS Service Discovery) publishes what each Pi can do.

How rpi-sync Uses Discovery

When rpi-sync runs, it advertises itself via Avahi. Other nodes can discover it without knowing its IP:

discover_services.py
#!/usr/bin/env python3 """Discover rpi-sync nodes on the LAN using Avahi.""" import subprocess import re def discover_rpi_sync_nodes(): """Find all rpi-sync services on the local network.""" nodes = [] # Browse for _rpi-sync._tcp services result = subprocess.run( ["avahi-browse", "-t", "-r", "_rpi-sync._tcp"], capture_output=True, text=True ) for line in result.stdout.splitlines(): if "=" in line and "rpi-sync" in line: # Parse: = eth0 IPv4 pi-workshop _rpi-sync._tcp local parts = line.split() if len(parts) >= 6: hostname = parts[3] nodes.append({ "hostname": hostname, "address": f"{hostname}.local" }) return nodes if __name__ == "__main__": nodes = discover_rpi_sync_nodes() for node in nodes: print(f"Found: {node['address']}")

Publishing a Service

To make your service discoverable, create an Avahi service file:

/etc/avahi/services/rpi-sync.service
<?xml version="1.0" standalone='no'?> <!DOCTYPE service-group SYSTEM "avahi-service.dtd"> <service-group> <name>Raspberry Pi File Sync</name> <service> <type>_rpi-sync._tcp</type> <port>22</port> <txt-record>version=2.0</txt-record> <txt-record>capabilities=sync,deploy,watch</txt-record> </service> </service-group>
Reload Avahi
sudo systemctl reload avahi-daemon
avahi-browse -t -r _rpi-sync._tcp
= eth0 IPv4 Raspberry Pi File Sync _rpi-sync._tcp local
hostname = [pi-workshop.local]
address = [192.168.1.101]
port = [22]
txt = ["version=2.0" "capabilities=sync,deploy,watch"]

Did this guide help?

Your answers shape what we write next.

Phase 3: Build a Service Mesh

With mDNS working, your Pis can form a service mesh — each node knows what every other node can do.

CoreConduit Service Mesh
pi-workshop
_rpi-sync._tcp
_rpi-monitor._tcp
pi-garden
_hydromazing._tcp
_rpi-sync._tcp
pi-nexus
_nexus-ai._tcp
_rpi-sync._tcp

Service Registry Pattern

Build a simple service registry that auto-updates as nodes join and leave:

service_registry.py
#!/usr/bin/env python3 """Auto-updating service registry for LAN services.""" import subprocess import json import threading import time from pathlib import Path class LANServiceRegistry: """Discovers and tracks services on the local network.""" def __init__(self, cache_file="~/.lan_registry.json"): self.cache_file = Path(cache_file).expanduser() self.services = {} self._running = False def discover(self, service_type=None): """Discover services. If type is None, discover all.""" cmd = ["avahi-browse", "-t", "-r", "-p"] if service_type: cmd.append(service_type) else: cmd.append("-a") # All services result = subprocess.run(cmd, capture_output=True, text=True) return self._parse_services(result.stdout) def get_service(self, name): """Get a service by name.""" self.refresh() return self.services.get(name) def get_by_type(self, service_type): """Get all services of a given type.""" return [ s for s in self.services.values() if s.get("type") == service_type ] def refresh(self): """Refresh service list from network.""" self.services = self.discover() self._save_cache() def _save_cache(self): with open(self.cache_file, "w") as f: json.dump(self.services, f, indent=2) def _parse_services(self, output): """Parse avahi-browse output.""" services = {} for line in output.strip().split("\n"): if line.startswith("="): # Parse resolved service line parts = line.split(";") if len(parts) >= 10: name = parts[3] services[name] = { "type": parts[4], "domain": parts[5], "hostname": parts[6], "address": parts[7], "port": int(parts[8]), "txt": parts[9:] } return services

Phase 4: Practical Integration

Now integrate discovery into your projects. Here's how rpi-sync uses it:

Auto-Discover Sync Peers

rpi-sync Discovery
rpi-sync discover
Scanning LAN for rpi-sync nodes...
Found 3 nodes:
pi-workshop.local (192.168.1.101) [rpi-sync v2.1]
pi-garden.local (192.168.1.102) [rpi-sync v2.0]
pi-nexus.local (192.168.1.103) [rpi-sync v2.1]
Add discovered nodes to config? [Y/n] y
✓ Added 3 nodes to ~/.rpi-sync/rpi-sync.conf

Auto-Configure rpi-monitor Hub

When running rpi-monitor in hub mode, it can discover agents automatically:

services.json (auto-generated)
{ "agents": [ { "name": "pi-workshop", "host": "pi-workshop.local", "port": 8585, "discovered": true }, { "name": "pi-garden", "host": "pi-garden.local", "port": 8585, "discovered": true } ] }

Phase 5: Troubleshooting Discovery

Discovery Not Working?

mDNS is multicast-based and can be blocked by network equipment or firewalls.

Debug mDNS Issues
# Check if Avahi is running
sudo systemctl status avahi-daemon
# Check firewall (UDP port 5353)
sudo ufw status | grep 5353
sudo ufw allow 5353/udp
# Test local resolution
avahi-resolve-host-name pi-workshop.local
pi-workshop.local 192.168.1.101
# Monitor mDNS traffic
sudo tcpdump -i eth0 udp port 5353

Common Issues

.local not resolving Install libnss-mdns: sudo apt install libnss-mdns
Windows can't see .local Install Bonjour or use the IP directly
Multicast blocked Some routers block mDNS. Enable IGMP snooping or use static IPs as fallback
Docker containers Use --network host mode or install avahi-daemon in container

Security Considerations

mDNS operates on the local network only — it doesn't traverse routers or the internet. This is a feature for self-hosted setups, but be aware:

  • LAN-only — Anyone on your network can discover your services
  • No authentication — mDNS is purely informational; services must handle auth
  • Spoofing possible — A malicious device could impersonate another service

For sensitive services, layer additional authentication (SSH keys, API tokens) on top of discovery.

Next Steps

Get the Code

Full implementations in rpi-sync (LAN discovery for file sync) and rpi-monitor (hub mode with auto-discovered agents).