Help with Local DNS Resolution

Hi there, I’m using NixOS with services.avahi.enable and services.avahi.nssmdns set to true in my configuration.nix file.

When I run avahi-resolve -n example.local it returns the correct IP. However, other usages like ssh example.local or ping example.local result in errors like:
ssh: Could not resolve hostname example.local: Device or resource busy

/run/wrappers/bin/ping: example.local: System error

Process Ping example.local exited abnormally with code 2

It seems like something is preventing avahi from resolving the hostname to an IP address in these cases. How might I fix this?

1 Like

I have the same problem!

I found disabling the firewall seemed to help, but have not been able to figure out why…

The .local top-level-domain is unique. Lookups are resolved through mDNS, which listens on port 5353. The local mDNS daemon collects information about available network services through this port.

If the local firewall service is not configured to support mDNS, then the machine will not know about network services advertising as .local devices. Ideally enabling avahi would be enough for the firewall to support it, but maybe the nixOS implementation is incomplete, or you have something else overriding it.

Similarly, for the local host to lookup .local addresses, the name service resolver must be configured to use mDNS before the traditional lookup path. Again, this should happen automatically with nixOS, but local resolver configuration is complicated enough that accidentally overriding proper .local lookup seems too likely when configuring other name resolution services.

Most home routers use .lan as a top-level-domain for devices on the local network, along with the device host name supplied in its DHCP request or manually assigned in the router config. You might find that .lan just works on your network, as in ping my-other-machine.lan.

To investigate why .local is not working, I’d first thoroughly review all network-related bits of the OS config. Often the nixOS module info is insufficient, requiring studying the module source code, Arch Linux Wiki, and the man-pages for the relevant services to understand what each bit is doing and how they might interact. Sometimes also there is useful info available from sudo journalctl -b and sudo journalctl -f.

Unless you’ve disabled services.avahi.openFirewall, the port should be open.

I never figured out what the firewall problem was all about; the port was open. I found that resolving .local names would work for a few minutes after restarting avahi (usually a result of doing a nixos-rebuild switch), but give it a few minutes and it’d silently stop working.
I enabled the firewall debug settings and couldn’t figure out what was going wrong with it. I’m afraid I don’t have those to hand, but it’s immaterial to my current problem.

In the end, I just disabled the firewall and that was enough for me, until recently where it seems like resolution is intermittently failing, even for the current host:

# ping pea.local
ping: pea.local: System error
... wait ...
# ping pea.local
PING pea ( 56(84) bytes of data.
64 bytes from pea ( icmp_seq=1 ttl=64 time=0.117 ms

FWIW My router does not appear to resolve local devices as either .local or .lan, so I don’t think it’s interfering. .local names have been very reliable on Ubuntu and Debian devices on my network. Maybe I will try and compare configs…

The first Debian device I checked used mdns4_minimal only. I looked into this and the 4 means IPv4. On NixOS, avahi.nssmdns uses mdns which apparently tries IPv6 first and then falls back to IPv4.

IPv6 has caused me no end of pain the past so this sounded suspicious. My network also doesn’t support IPv6 anyway and even if it did, I’d be happy to use IPv4 since it’s all LAN anyway.

For this reason I disabled avahi.mssdns and instead copied its definition, using mdns4 instead.

  # this is what avahi.nssmdns does, but mdns4 (IPv4) instead of mdns (dual-stack)
  system.nssModules = pkgs.lib.optional true pkgs.nssmdns;
  system.nssDatabases.hosts = pkgs.lib.optionals true (pkgs.lib.mkMerge [
    (pkgs.lib.mkBefore [ "mdns4_minimal [NOTFOUND=return]" ]) # before resolve
    (pkgs.lib.mkAfter [ "mdns4" ]) # after dns

Immediately after instating this configuration, it’s been working first time every time, but I will have to test it over a longer period of time to truly have any confidence in whether that’s fixed it or not.

1 Like