Inspect and Configure systemd-resolved over D-Bus
You will finish with a safe way to inspect the org.freedesktop.resolve1 service, translate a Linux interface index into a resolver link object, and understand the per-link calls used to change DNS behaviour. The examples target systemd 255, installed here as Ubuntu package build 255.4-1ubuntu8.17.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about twenty minutes. You need gdbus, ip and a running system bus. Read-only inspection normally needs no elevated privileges. Changing resolver settings can interrupt name lookups and may be overwritten by NetworkManager or systemd-networkd, so take a maintenance window and keep the previous state available before making a change.
1. Confirm the local API and service
Check the installed systemd version and the resolver helper first. These are ordinary, read-only commands:
$ systemctl --version | head -n 1
systemd 255 (255.4-1ubuntu8.17)
$ command -v gdbus
/usr/bin/gdbus
$ resolvectl status
The last command prints the resolver's current global and per-link state. Your DNS servers, interface names and protocol settings will differ. A service that is not running may still leave another component answering through a different resolver path, but D-Bus calls to this name will not work until systemd-resolved.service is available.
Checkpoint: if the status command reports a resolver state you do not recognise, stop here and identify which service owns DNS on the host. Do not configure a second manager over the top of it.
2. Discover the Manager interface
The manager has a fixed bus name and object path. Ask the system bus for its exported methods and properties:
$ gdbus introspect --system \
--dest org.freedesktop.resolve1 \
--object-path /org/freedesktop/resolve1
Look for org.freedesktop.resolve1.Manager. It contains resolver methods such as ResolveHostname, configuration methods such as SetLinkDNS, and read-only properties including DNS, CurrentDNSServer, Domains, DNSSEC and ResolvConfMode. Introspection output is the useful contract to compare with the installed manpage when a script must support more than one systemd release.
If you see Could not connect or a missing-name error, check the unit without changing it:
$ systemctl status systemd-resolved.service --no-pager
Starting or enabling a service is an administrative change and is deliberately outside this inspection workflow. If another network manager owns resolver configuration, fix that ownership decision before calling the D-Bus API.
3. Map an interface name to its link object
D-Bus methods use the numeric Linux interface index, not the friendly interface name. Find the index without changing network state:
$ ip -o link show dev enp0s31f6
2: enp0s31f6: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
Replace enp0s31f6 with the interface you actually intend to inspect. The leading number, 2 in this example, is the index. Ask the Manager for the corresponding object path:
$ gdbus call --system \
--dest org.freedesktop.resolve1 \
--object-path /org/freedesktop/resolve1 \
--method org.freedesktop.resolve1.Manager.GetLink 2
(objectpath '/org/freedesktop/resolve1/link/_2',)
The path encodes the index, but use the returned value rather than constructing it blindly. Interface indexes can be reused after a device disappears.
4. Inspect the link before touching it
Use the returned object path to inspect link-specific methods and properties:
$ gdbus introspect --system \
--dest org.freedesktop.resolve1 \
--object-path /org/freedesktop/resolve1/link/_2
Pay attention to DNS, DNSEx, Domains, DefaultRoute, LLMNR, MulticastDNS, DNSOverTLS and DNSSEC. The link's ScopesMask describes protocols currently usable on that interface; the protocol properties describe configured policy. Those are different questions, so a configured option does not guarantee that the interface is currently suitable.
Record the current state in your change ticket, or save the relevant introspection output:
$ gdbus introspect --system \
--dest org.freedesktop.resolve1 \
--object-path /org/freedesktop/resolve1/link/_2 \
> resolve1-link-2-before.txt
The file is ordinary local evidence, not a restoration script. Values supplied by DHCP or another network manager may return when the link reconnects.
5. Understand the configuration calls
The Manager provides one-step calls that take an interface index, avoiding a separate GetLink round trip. SetLinkDNS accepts an array of address records. Each record contains an address family and raw address bytes, so an IPv4 server is four bytes and an IPv6 server is sixteen. SetLinkDNSEx additionally accepts a port and DNS name, which matters for DNS-over-TLS SNI.
SetLinkDomains takes domains paired with a boolean. A false value makes a search domain; a true value makes a routing-only domain. Search domains can qualify single-label names. Routing domains select where matching queries go but do not qualify those names. SetLinkDefaultRoute, SetLinkDNSSEC, SetLinkDNSOverTLS, SetLinkLLMNR and SetLinkMulticastDNS change the corresponding per-link policy.
Do not paste a made-up server into a live link. The following is a shape check with placeholders, not a command to run unchanged:
$ gdbus call --system \
--dest org.freedesktop.resolve1 \
--object-path /org/freedesktop/resolve1 \
--method org.freedesktop.resolve1.Manager.SetLinkDNS \
<IFINDEX> "[(<AF_INET>, [<IPV4_BYTE_1>, <IPV4_BYTE_2>, <IPV4_BYTE_3>, <IPV4_BYTE_4>])]"
For a real change, obtain the address family constants and byte values from the API or an established network-management tool, then verify with resolvectl status. A syntax error is safer than guessing, but a valid address can still make the host unable to resolve names.
6. Make a reversible per-link change
Only continue after confirming that this D-Bus API is the intended configuration owner. Use an approved DNS address and the exact interface index. The call below changes live state and may affect applications immediately:
$ sudo gdbus call --system \
--dest org.freedesktop.resolve1 \
--object-path /org/freedesktop/resolve1 \
--method org.freedesktop.resolve1.Manager.SetLinkDNS \
<IFINDEX> "[(<AF_INET>, [<IPV4_BYTE_1>, <IPV4_BYTE_2>, <IPV4_BYTE_3>, <IPV4_BYTE_4>])]"
()
Use elevated privileges only if the local bus policy requires them. Immediately check the result:
$ resolvectl status <INTERFACE>
Link <IFINDEX> (<INTERFACE>)
DNS Servers: <APPROVED_DNS_SERVER>
If the result is wrong, or the network manager reports ownership, stop testing and restore the previous configuration through its normal interface. For settings owned directly by this API, RevertLink resets all per-link settings to defaults, but it is not a general undo for DHCP or network-manager state. Treat it as a deliberate administrative action:
$ sudo gdbus call --system \
--dest org.freedesktop.resolve1 \
--object-path /org/freedesktop/resolve1 \
--method org.freedesktop.resolve1.Manager.RevertLink \
<IFINDEX>
()
7. Diagnose failures without guessing
NoSuchLink means the interface index no longer exists. Re-run ip -o link; do not reuse a stale number. LinkBusy means systemd-networkd has already taken possession of the interface and supplied its configuration. Change the owning manager instead. NoNameServers means no suitable DNS server was found, while NetworkDown means no suitable connected network was available.
For query methods, distinguish a DNS answer from a local API failure. NoSuchRR is the DNS NODATA case, DnsError.NXDOMAIN means the server reported a negative result, and DnssecFailed means validation rejected the acquired response. Resource records arrive from the network and must be validated by the client before use. Passing flags as zero is the documented default for resolver calls unless your client has a specific reason to request a protocol or restriction.
Done means
- You confirmed the installed systemd 255 API and resolver service state.
- You used the live interface index, then obtained the matching D-Bus link path.
- You inspected Manager and Link properties before changing anything.
- You know which settings are search domains, routing domains and default-route policy.
- Any live DNS change was authorised, verified with
resolvectl, and has a recovery path. - You can distinguish a stale link, competing network manager and DNS response error.