Why per‑interface search domains matter
If a box has more than one NIC—like a wired LAN that talks to a corporate DNS and a Wi‑Fi that talks to a public ISP—mixing the two can bite you. A query for server will be sent to the DNS server of the interface that sent the request, but the search list that the resolver appends is taken from the global configuration unless you tell it otherwise. That means you can end up asking your public DNS for an internal host or, even worse, leaking internal names to the Internet. Per‑interface search domains keep the resolver tidy and stop accidental leakage.
Prerequisites
Ubuntu 24.04 ships with systemd‑resolved as the default resolver and systemd‑networkd for static interfaces. NetworkManager can also drive systemd‑resolved. The examples below assume you have root access and that systemd-resolved is active (systemctl status systemd-resolved shows “active (running)”).
sudo systemctl enable --now systemd-resolved
The default /etc/resolv.conf is a symlink to /run/systemd/resolve/stub-resolv.conf. Keep that symlink; editing the file directly will be overwritten.
Using systemd‑networkd
Create a drop‑in directory for per‑interface settings:
sudo mkdir -p /etc/systemd/networkd.conf.d
For each interface, add a .network file. Example for eth0 that should use the corporate DNS and search domain corp.example.com:
# /etc/systemd/networkd.conf.d/eth0.network
[Match]
Name=eth0
[Network]
DNS=10.0.0.1
Domains=corp.example.com
Similarly, for wlan0 that should use the ISP’s DNS and search domain home.net:
# /etc/systemd/networkd.conf.d/wlan0.network
[Match]
Name=wlan0
[Network]
DNS=8.8.8.8
Domains=home.net
Reload networkd and systemd‑resolved:
sudo systemctl restart systemd-networkd
sudo systemctl restart systemd-resolved
Why this works
systemd‑resolved consults the Domains= key in the .network file for the interface that receives the query. The search list is then limited to that domain, and the DNS server chosen is per‑interface, so queries never leave the intended network.
Using NetworkManager
If you prefer NetworkManager, you can set the search domain in the connection editor or via nmcli.
# For eth0
nmcli con modify eth0 ipv4.dns-search "corp.example.com"
nmcli con up eth0
# For wlan0
nmcli con modify wlan0 ipv4.dns-search "home.net"
nmcli con up wlan0
NetworkManager writes these settings to /etc/NetworkManager/system-connections/*.nmconnection. The dns-search key is translated into the Domains= option that systemd‑resolved consumes.
Verifying the configuration
Run systemd-resolve --status to see per‑interface settings:
$ systemd-resolve --status
Global
DNS Servers: 8.8.8.8
DNSSEC supported: yes
DNSSEC enabled: no
DNSSEC trust anchors: 0
DNSSEC supported: yes
Interface eth0
Current DNS Server: 10.0.0.1
DNS Servers: 10.0.0.1
Domains: corp.example.com
Interface wlan0
Current DNS Server: 8.8.8.8
DNS Servers: 8.8.8.8
Domains: home.net
Test resolution with dig or systemd-resolve:
systemd-resolve -i eth0 -d corp.example.com server
systemd-resolve -i wlan0 -d home.net server
Both should return the IP address from the respective DNS servers.
Security considerations
Split DNS – By keeping internal and external DNS servers separate, you prevent internal hostnames from leaking to the public DNS. This is especially important in hybrid environments where a public interface might inadvertently send queries to the internal DNS.
DNSSEC – Enable DNSSEC on the internal DNS if possible. systemd‑resolved can enforce DNSSEC per‑interface by adding
DNSSEC=yesin the.networkfile. This ensures that only authenticated responses are accepted.Least privilege – If you only need a search domain for a single interface, avoid global
Domains=entries in/etc/systemd/resolved.conf. Keep the global search list empty (Domains=) and rely on per‑interface files.Avoid DNS over UDP – For sensitive environments, consider configuring systemd‑resolved to use DNS over TLS (DOT) per interface by adding
DNSOverTLS=yesin the.networkfile. This protects queries from eavesdropping on untrusted networks.
Common pitfalls and troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
systemd-resolve --status shows no DNS server for an interface | Interface not matched by any .network file | Verify Name= matches the actual interface name (ip link show). |
| Queries resolve to wrong domain | Global Domains= still set in /etc/systemd/resolved.conf | Clear the global entry (Domains=) or set it to ~ to inherit from interfaces. |
/etc/resolv.conf is a regular file | Another tool (e.g., resolvconf) overwrote the symlink | Remove the file and recreate the symlink: sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf. |
| DNSSEC validation fails | Internal DNS not signed or trust anchors missing | Disable DNSSEC for that interface (DNSSEC=no) or add the proper trust anchor. |
systemd-resolve shows “DNSSEC supported: yes” but queries fail | The DNS server does not support DNSSEC | Verify the server’s configuration or disable DNSSEC for that interface. |
Use journalctl -u systemd-resolved for detailed logs if resolution stalls.
Trade‑offs
- systemd‑networkd gives you fine‑grained control and is ideal for servers or static setups. It requires editing files directly but is lightweight.
- NetworkManager is more user‑friendly for desktops or laptops that switch between networks. It automatically updates systemd‑resolved but can be less transparent.
- If you need to push DNS settings to a large fleet, consider using
systemd-networkdwith templated.networkfiles or a configuration management tool; that keeps everything declarative.
The key takeaway? Don’t rely on a single global search list when you have multiple NICs. Explicit per‑interface settings keep name resolution predictable, secure, and easier to debug.
TAGS: linux dns systemd network security
See also
- Using journalctl to Track Down the Hidden ‘eth0’ Carrier Lost Messages That Cause Network Flaps After a Kernel Upgrade
- Rebuilding initramfs to exit emergency mode after a kernel update on Ubuntu 24.04
- How to Extract a Single File from a Borg Backup Archive Without Recreating the Entire Directory Tree
- Configure systemd’s OnFailure to email me when my daily backup service dies
- When chmod 2775 Turns Into a Security Hole: Fixing Setgid Misconfigurations on /srv/shared