The direct answer
It doesn't. systemd-resolved has no mechanism to enforce consistency between its internal routing state and /etc/resolv.conf. It writes two managed files — /run/systemd/resolve/stub-resolv.conf (advertising the stub at 127.0.0.53) and /run/systemd/resolve/resolv.conf (listing upstream servers directly) — and whatever /etc/resolv.conf actually contains is what the glibc resolver uses. If that file is static, hand-edited, or bind-mounted into a container, resolved's per-link routing, split-DNS, and caching are silently ignored by anything resolving through libc.
What each configuration actually gives you
| /etc/resolv.conf points to | Behavior |
stub-resolv.conf | Queries go to 127.0.0.53; full per-link domain routing, caching, LLMNR/mDNS, DNSSEC signaling via trust-ad |
resolv.conf (non-stub) | Correct upstream servers, but no split-DNS, no cache, no per-link routing |
| A regular/static file | Frozen in time; resolved keeps working via D-Bus but libc lookups ignore it entirely |
Resolved will log a warning at startup if /etc/resolv.conf is not one of its known symlink targets — that journal message is the only built-in signal you get.
Indicators that an application is bypassing the stub
- Counter test: run
resolvectl statistics, perform a lookup from the suspect application, and re-check. If the transaction/cache counters do not increment, the query never reached the stub.
- Routing mismatch:
resolvectl query some.internal.name succeeds (resolved would route it to the right link) but dig some.internal.name or the application fails. That gap means libc is reading a different server list than resolved's routing table.
- Packet capture:
sudo tcpdump -i any port 53 while resolving. Traffic going straight to upstream servers instead of 127.0.0.53 confirms bypass.
Note that some bypass is by design: browsers with DNS-over-HTTPS enabled, Go binaries using their own resolver in some modes, and containers with a Docker-generated resolv.conf never consult the stub. Inside a container, 127.0.0.53 is unreachable anyway — the stub listener is host-local, so do not hardcode it there.
Restoring consistency
This is an administrator action, not something resolved repairs itself:
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved
Two caveats: glibc caches resolver state per process, so already-running applications may not pick up the change until restarted; and exact defaults (symlink target, options written) vary across systemd and Fedora releases — verify with ls -l /etc/resolv.conf and resolvectl status on your system rather than assuming.