
How do I publish a custom service with AVAHI using an XML file in /etc/avahi/services (example for HTTP on port 8080)?
Avahi’s XML service definitions are the simplest way to publish a static service on a Linux LAN without writing any D-Bus client code. You drop an XML file into /etc/avahi/services, Avahi reloads it, and suddenly every Bonjour/Zeroconf-aware system can see your service over mDNS/DNS-SD.
Below I’ll walk through a concrete example: publishing an HTTP service on port 8080 with an XML file, plus how to verify it works and what to check when it doesn’t.
What this approach is (and isn’t)
Avahi is a system for service discovery on local networks via the mDNS/DNS-SD protocol suite. On most Linux distributions it ships by default, targets Linux first, and interoperates with Bonjour/Zeroconf on other devices.
There are two main integration paths:
- D-Bus API (primary interface, required for most dynamic usage)
- XML service definitions in
/etc/avahi/services(static service publication)
In this guide we’re using the XML route. It’s ideal for:
- Services that always run on the same host/port (e.g., a local HTTP service on 8080)
- Packaged daemons where your distro or deployment system can drop a file into
/etc/avahi/services - Environments where you don’t want to ship or maintain a custom Avahi/D-Bus client
If you need to dynamically change ports, TXT records, or service lifetimes at runtime, you’d use the D-Bus API instead.
Prerequisites
Before creating the XML file, confirm the basics:
1. Avahi is installed and running
On a typical systemd-based distribution:
systemctl status avahi-daemon.service
You want to see active (running). If not:
sudo systemctl enable --now avahi-daemon.service
On non-systemd systems, use the init system your distro provides (e.g., /etc/init.d/avahi-daemon start or equivalent).
2. You have root access
You’ll be writing into:
/etc/avahi/services/
You need sufficient privileges (e.g., via sudo) to create files there.
3. Your HTTP service actually listens on port 8080
Make sure there is a real service behind the advertisement:
sudo ss -tlnp | grep ':8080'
# or
sudo netstat -tlnp | grep ':8080'
You should see a process bound to 0.0.0.0:8080 or at least to your LAN-facing IP. Avahi doesn’t validate the service; it only announces it.
Step-by-step: publish HTTP on port 8080 with an XML file
1. Create the services directory (if it doesn’t exist)
Most distros ship this directory already. If not:
sudo mkdir -p /etc/avahi/services
Permissions typically look like:
ls -ld /etc/avahi/services
# drwxr-xr-x root root ...
Leave ownership as root:root with world-readable files inside.
2. Create your XML service file
We’ll define a simple HTTP service pointing to port 8080.
Create /etc/avahi/services/http-8080.service with your editor of choice:
sudo nano /etc/avahi/services/http-8080.service
Paste the following:
<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name replace-wildcards="yes">%h HTTP on 8080</name>
<service>
<type>_http._tcp</type>
<port>8080</port>
<txt-record>path=/</txt-record>
</service>
</service-group>
Save and exit.
What each element means
-
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
Points to the Avahi service DTD; ensures the structure is valid. -
<service-group>
Top-level container. You can put one or more<service>entries here that share a common name. -
<name replace-wildcards="yes">%h HTTP on 8080</name>%his replaced by the local hostname as known to Avahi (e.g.,myhost→myhost HTTP on 8080).replace-wildcards="yes"instructs Avahi to perform that substitution.
-
<type>_http._tcp</type>
Service type in DNS-SD form._http._tcpis the standard for HTTP. Other examples:_ipp._tcpfor IPP printers,_ssh._tcpfor SSH. -
<port>8080</port>
The TCP port your service listens on. -
<txt-record>path=/</txt-record>
Optional TXT record key-value data. For HTTP,path=/is a common convention. You can add moretxt-recordlines if needed.
3. Check syntax and permissions
Verify the file is readable by Avahi:
ls -l /etc/avahi/services/http-8080.service
# -rw-r--r-- 1 root root ... /etc/avahi/services/http-8080.service
Ensure it’s plain text XML and not world-writable.
If you introduce syntax errors, Avahi will log them (see troubleshooting below).
4. Reload or restart Avahi
Many distributions watch /etc/avahi/services and reload automatically when files change, but I prefer an explicit restart to be certain:
sudo systemctl restart avahi-daemon.service
On non-systemd systems, use the equivalent service restart command.
Verifying that the service is published
To confirm that Avahi is actually advertising your HTTP-on-8080 service via mDNS/DNS-SD, use one or more of the following methods.
1. On the same Linux host: use avahi-browse
avahi-browse is usually provided by the avahi-utils or avahi tools package.
Install if necessary:
# Debian/Ubuntu
sudo apt-get install avahi-utils
# Fedora/RHEL/CentOS
sudo dnf install avahi-tools
# Arch
sudo pacman -S avahi
Then browse for HTTP services:
avahi-browse -rt _http._tcp
You should see output similar to:
+ eth0 IPv4 myhost HTTP on 8080 _http._tcp local
= eth0 IPv4 myhost HTTP on 8080 _http._tcp local
hostname = [myhost.local]
address = [192.168.1.50]
port = [8080]
txt = ["path=/"]
Key checks:
- The service name matches
%h HTTP on 8080with your hostname substituted. port = [8080].txt = ["path=/"]shows the TXT record you configured.
2. From another Linux machine: avahi-browse over the LAN
On another Linux host on the same local network (with mDNS allowed through firewalls):
avahi-browse -rt _http._tcp
You should see the same service resolved to the IP of the advertising host.
If you see nothing from other machines but you do see the service locally, it often indicates:
- Firewall blocking mDNS (UDP 5353 multicast) or
- Multicast being filtered on the network.
3. From macOS: use dns-sd
On macOS, open Terminal:
dns-sd -B _http._tcp local
You should see your service appear. To resolve details:
dns-sd -L "myhost HTTP on 8080" _http._tcp local
The service details should match the Avahi output, including the port and TXT record.
Optional: make hostname.local resolve with nss-mdns
The XML file publishes a DNS-SD service via mDNS. If you also want hostname.local lookups (e.g., curl http://myhost.local:8080/ from any Linux box on the LAN), you’ll typically pair Avahi with nss-mdns.
nss-mdns plugs into nsswitch so that *.local hostnames are resolved via mDNS in all system programs.
1. Install nss-mdns
Package names vary:
# Debian/Ubuntu
sudo apt-get install libnss-mdns
# Fedora
sudo dnf install nss-mdns
# Arch
sudo pacman -S nss-mdns
2. Update /etc/nsswitch.conf
Look for the hosts: line. A common baseline is:
hosts: files mdns_minimal [NOTFOUND=return] dns
Or, if you want more complete mDNS handling:
hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4
Refer to your distro’s nss-mdns documentation for the preferred configuration.
With nss-mdns and Avahi running, ping myhost.local and curl http://myhost.local:8080/ should just work from other Linux machines on the LAN.
Extending the example: multiple services and TXT records
Once you’re comfortable with a single HTTP service, you can extend the XML for more complex scenarios.
Multiple TXT records for HTTP
If your HTTP service needs more metadata:
<service-group>
<name replace-wildcards="yes">%h HTTP on 8080</name>
<service>
<type>_http._tcp</type>
<port>8080</port>
<txt-record>path=/</txt-record>
<txt-record>version=1.0</txt-record>
<txt-record>role=api</txt-record>
</service>
</service-group>
Avahi 0.7 added support for encoding binary (non-text) TXT records into XML service definitions, but typical HTTP use cases stick to ASCII text key-value pairs.
Publish more than one service from the same host
You can either:
- Put multiple
<service>entries in a single<service-group>, or - Create separate
.servicefiles under/etc/avahi/services.
Example with two services in one file:
<service-group>
<name replace-wildcards="yes">%h Services</name>
<service>
<type>_http._tcp</type>
<port>8080</port>
<txt-record>role=web</txt-record>
</service>
<service>
<type>_ssh._tcp</type>
<port>22</port>
</service>
</service-group>
Both services will share the same base name (%h Services) but appear under their respective types (_http._tcp, _ssh._tcp).
Common issues and how to debug them
When “I dropped a file into /etc/avahi/services but nothing shows up” happens, I usually check the following.
1. XML syntax or structure errors
Avahi logs parse errors. On systemd-based systems:
journalctl -u avahi-daemon.service -b
Look for lines mentioning the filename and XML errors.
Typical problems:
- Missing closing tags
- Wrong root element (must be
<service-group>) - Typo in the DTD line
Fix the file, save, and restart Avahi:
sudo systemctl restart avahi-daemon.service
2. Service type or port mistakes
If the type is wrong, clients won’t discover it under the expected type:
- For HTTP, the type must be
_http._tcp. - Don’t forget the leading underscore and
_tcp.
Double-check the port:
<port>8080</port>
If your actual service runs on a different port than you configured, discovery will work, but clients will fail to connect. Service discovery doesn’t auto-detect ports; it announces whatever you specify.
3. Avahi not running or blocked by firewall
Ensure Avahi is active:
systemctl status avahi-daemon.service
If other hosts can’t see the service:
- Confirm the firewall allows UDP 5353 multicast and relevant unicast responses on the advertising host.
- Some firewall frontends have specific “mDNS” or “Avahi” service toggles.
4. Network or VLAN boundaries
mDNS is multicast scoped to the local link. If clients and the advertising host are on different broadcast domains or VLANs without mDNS relaying/proxying, they will not see each other.
For basic setups, ensure:
- The advertising host and clients are on the same Wi-Fi/ethernet segment.
- There is no layer-2 segmentation filtering multicast.
When to switch from XML to the D-Bus API
The /etc/avahi/services method is intentionally static. It’s a good fit when:
- You have a well-known service port.
- You want to ship a simple, distro-friendly integration.
- Configuration management (Ansible, Puppet, system packages) can drop the XML file for you.
Use the D-Bus API instead when:
- Ports or TXT records change at runtime (e.g., ephemeral ports).
- You want to publish services only while a process is actually running and tear them down programmatically.
- You need to react to Avahi server state changes (e.g., hostname collisions,
AVAHI_SERVER_REGISTERING).
Avahi’s documentation includes “Choosing an API,” “How to Register Services,” and “How to Browse for Services” sections that walk through the D-Bus-based approach.
Summary
To publish a custom HTTP service on port 8080 with Avahi using an XML file in /etc/avahi/services:
-
Ensure
avahi-daemonis installed and running. -
Confirm your HTTP service listens on port 8080.
-
Create
/etc/avahi/services/http-8080.servicewith a valid XML definition:<?xml version="1.0" standalone='no'?> <!DOCTYPE service-group SYSTEM "avahi-service.dtd"> <service-group> <name replace-wildcards="yes">%h HTTP on 8080</name> <service> <type>_http._tcp</type> <port>8080</port> <txt-record>path=/</txt-record> </service> </service-group> -
Restart Avahi (
systemctl restart avahi-daemon.service). -
Verify with
avahi-browse -rt _http._tcpor equivalent tools on other hosts (ordns-sdon macOS). -
Optionally install nss-mdns and adjust
nsswitch.confsohostname.localresolves everywhere.
Once this is in place, your service behaves like any Bonjour/Zeroconf-advertised HTTP endpoint on the LAN: peers can discover it automatically without needing IP addresses or manual port documentation.