
How do I register/unregister a service dynamically using the AVAHI D-Bus API (and what’s the simplest example flow)?
Most applications that “speak Avahi” at runtime should be using the D-Bus API, not embedding a second mDNS stack. That’s exactly what the Avahi daemon is for: you register and withdraw services over D-Bus, and Avahi handles mDNS/DNS-SD publication, host name collisions, and daemon restarts for you.
Below is a practical, minimal flow for dynamically registering and unregistering a service with the Avahi D-Bus API, plus a concrete example in Python. I’ll walk through the mechanics first, then show the simplest end-to-end code.
What “dynamic registration over D-Bus” actually means
When you use the Avahi D-Bus API, you’re:
- Talking to the
avahi-daemonprocess (the mDNS/DNS-SD stack) via system D-Bus. - Creating an EntryGroup object that holds one or more DNS-SD records (your service).
- Asking Avahi to commit that group so it’s published on the LAN.
- Optionally resetting or destroying that group later to withdraw the service.
This is the D-Bus equivalent of the C flow documented for avahi_entry_group_new() / avahi_entry_group_add_service() / commit/reset.
High‑level runtime story:
- Connect to D-Bus.
- Get the Avahi Server object.
- Create an EntryGroup.
- Add your service (
_http._tcp,_ssh._tcp, etc.) to that group. - Commit the group (service becomes visible via mDNS/DNS-SD).
- When done, reset or free the group (service disappears).
Minimal D-Bus objects and interfaces involved
On the system bus, the Avahi daemon exposes:
- Bus name:
org.freedesktop.Avahi - Server object path:
/ - Server interface:
org.freedesktop.Avahi.Server - EntryGroup interface:
org.freedesktop.Avahi.EntryGroup
The sequence for dynamic registration is:
- Call
Server.GetNetworkInterfaceIndexByName()(optional) or use-1for “all interfaces”. - Call
Server.EntryGroupNew()to get anEntryGroupobject path. - On that
EntryGroup, callAddService()/AddServiceTxt()or their variants. - On that
EntryGroup, callCommit(). - Later, call
Reset()orFree()to remove the service.
On the wire, Avahi will then broadcast the appropriate mDNS records (SRV, TXT, A/AAAA) for your service.
Core parameters you need to decide
For AddService() / AddServiceTxt() you must pick:
- Interface / protocol
-1/-1for “all interfaces / any protocol” is the usual choice.
- Flags
0for “no special flags” is fine for most basic services.
- Service name
- Human-facing name, e.g.
"My HTTP Service"or"Backup API on ${HOSTNAME}".
- Human-facing name, e.g.
- Service type
- DNS-SD type, e.g.
"_http._tcp","_ssh._tcp","_ipp._tcp","_afpovertcp._tcp".
- DNS-SD type, e.g.
- Domain
- Usually empty string
""for default (local).
- Usually empty string
- Host
- Usually empty string
""to use the host’s default mDNS name.
- Usually empty string
- Port
- TCP or UDP port your service listens on (e.g.
8080).
- TCP or UDP port your service listens on (e.g.
- TXT records
- Key=value pairs describing capabilities, e.g.
path=/,version=1. Can be empty.
- Key=value pairs describing capabilities, e.g.
Simplest example flow in plain steps
This is the bare-bones control flow for “register/unregister a service dynamically using the Avahi D-Bus API”:
-
Connect to system D-Bus
Use your language’s D-Bus bindings to connect to the system bus.
-
Get the Avahi Server object
- Bus name:
org.freedesktop.Avahi - Object path:
/ - Interface:
org.freedesktop.Avahi.Server
- Bus name:
-
Create an EntryGroup
- Call
EntryGroupNew()on theServerobject. - This returns an object path, e.g.
/Client1/EntryGroup1.
- Call
-
Add your service
On the
EntryGroupobject (interfaceorg.freedesktop.Avahi.EntryGroup), call:AddService(interface=-1, protocol=-1, flags=0, name="My HTTP Service", type="_http._tcp", domain="", host="", port=8080, txt=[])
or
AddServiceTxt()if you want to add TXT data in one call. -
Commit the EntryGroup
Still on the
EntryGroupobject:- Call
Commit().
At this point, Avahi announces your service on the local network via mDNS/DNS-SD. Tools like
avahi-browse -aordns-sd/dns-sd.pyshould see it. - Call
-
Unregister the service
When your application shuts down or wants to withdraw the service:
- Call
Reset()on theEntryGroupto remove all its records but keep the group object for future reuse, or - Call
Free()(if exposed by your binding) to destroy the group.
When the process exits or disconnects from D-Bus, Avahi will also clean up its registrations.
- Call
Concrete example: Python, system D-Bus, _http._tcp service
This example uses dbus-python, which is a thin binding around libdbus. It matches the internal C flow (EntryGroup → add service → commit → reset).
1. Basic registration script
#!/usr/bin/env python3
import dbus
import time
import signal
import sys
BUS_NAME = "org.freedesktop.Avahi"
SERVER_PATH = "/"
SERVER_IFACE = "org.freedesktop.Avahi.Server"
ENTRYGROUP_IFACE = "org.freedesktop.Avahi.EntryGroup"
def main():
# Connect to the *system* bus, where avahi-daemon lives
bus = dbus.SystemBus()
# Get the server proxy
server = dbus.Interface(
bus.get_object(BUS_NAME, SERVER_PATH),
SERVER_IFACE
)
# Create a new EntryGroup
entry_group_path = server.EntryGroupNew()
entry_group = dbus.Interface(
bus.get_object(BUS_NAME, entry_group_path),
ENTRYGROUP_IFACE
)
# Interface/protocol: -1 means "use all interfaces / all protocols"
interface = dbus.Int32(-1)
protocol = dbus.Int32(-1)
flags = dbus.UInt32(0)
service_name = "My HTTP Service (Avahi D-Bus)"
service_type = "_http._tcp"
domain = ""
host = ""
port = dbus.UInt16(8080)
# Example TXT record: version=1, path=/
txt = dbus.Array(
[
dbus.ByteArray(b"version=1"),
dbus.ByteArray(b"path=/"),
],
signature='ay'
)
# Add the service
entry_group.AddServiceTxt(
interface,
protocol,
flags,
service_name,
service_type,
domain,
host,
port,
txt
)
# Commit the group so the service becomes visible
entry_group.Commit()
print(f"Service registered: {service_name} on port {port}")
# Keep the process alive so the service stays published
def shutdown(signum, frame):
print("Unregistering service and exiting...")
try:
# Reset removes all records from this group
entry_group.Reset()
except dbus.DBusException as e:
print(f"Error during Reset(): {e}")
sys.exit(0)
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)
# Boring sleep loop; in a real app you would integrate with your main loop
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
shutdown(None, None)
if __name__ == "__main__":
main()
2. How to see that it works
On another terminal on the same LAN (or another host on the same multicast segment):
# Browse for http services
avahi-browse -rt _http._tcp
You should see a service with the name My HTTP Service (Avahi D-Bus) and port 8080. When you Ctrl+C the Python service, the script calls entry_group.Reset(), and the service disappears from avahi-browse output.
Handling daemon restarts and collisions (the “real” flow)
The example above keeps things intentionally minimal. In a real deployment you should handle:
1. avahi-daemon restarts
If the daemon is restarted underneath you, its D-Bus objects vanish and are recreated. The C docs call this out explicitly under “How to Write a Client That Can Deal with Daemon Restarts.”
Over D-Bus, the pattern is:
- Listen for
NameOwnerChangedonorg.freedesktop.DBusfororg.freedesktop.Avahi. - When Avahi reappears, re‑create your
Serverproxy, allocate a newEntryGroup, re‑add your services, andCommit()again.
Most higher-level bindings provide some way to attach to D-Bus signals; the logic is the same as the C guidance: treat a daemon restart as a cue to rebuild your entries.
2. Name collisions
When the host’s .local name or your service name collides, Avahi may:
- Change the host name (e.g.
hostname.local→hostname-2.local), or - Auto-rename your service (e.g. append
(2)), or - Enter
AVAHI_SERVER_COLLISION/AVAHI_SERVER_REGISTERINGstates and then settle on a new, non-conflicting name.
The C docs emphasize:
move your services when the server enters
AVAHI_SERVER_COLLISIONorAVAHI_SERVER_REGISTERINGstate. Your services may not be reachable anymore since the local host name is no longer established or is currently in the process of being established.
On D-Bus, watch StateChanged (or equivalent) signals on the Server object and respond by:
- Temporarily resetting or suspending your entry groups when the server is in collision/transition.
- Re‑committing them once the server reports
AVAHI_SERVER_RUNNING.
Not all bindings expose the constants directly; you may have to map the integer state values yourself using the Avahi headers/docs.
When to prefer D-Bus over the C core API
Avahi’s own docs draw the line this way:
- C “core API” (libavahi-core): For embedded appliances or special cases where you need to embed the mDNS/DNS-SD stack directly. The docs explicitly say they discourage running multiple mDNS stacks on one host.
- D-Bus API: “We recommend using this API for software written in any language other than C (e.g. Python).” It talks to
avahi-daemonand avoids multiple stacks, and it’s the right choice for most desktop/server apps.
If your code isn’t a specialized appliance, stick with the D-Bus API. The dynamic registration/unregistration flow you’ve seen here is the intended pattern.
Putting it together as a repeatable recipe
To summarize the simplest dynamic service lifecycle over the Avahi D-Bus API:
- Connect to system D-Bus and get
org.freedesktop.Avahiserver at/. - Create an EntryGroup via
Server.EntryGroupNew(). - Add your service using
EntryGroup.AddService()orAddServiceTxt()with:interface=-1,protocol=-1,flags=0- Service
name,type(e.g._http._tcp),domain="",host="" portand optional TXT key/value pairs.
- Commit the EntryGroup with
EntryGroup.Commit(). - Keep your process (or main loop) alive; the service is now discoverable via mDNS/DNS-SD.
- Unregister when you’re done:
- Call
EntryGroup.Reset()(to remove all records), or simply exit and let Avahi clean up on D-Bus disconnect.
- Call
- For robust behavior:
- Watch for daemon restarts and rebuild your entry groups.
- Watch server state changes and adapt around collisions.
From there, you can scale from the simple _http._tcp example to whatever service types and TXT payloads your stack needs, while still using the same D-Bus registration/unregistration flow.