After connecting to WireGuard, you may sometimes want only internal domain names to be resolved through the DNS in the tunnel, while other domain names continue to use public DNS. On Linux systems already using systemd-resolved, you can directly configure DNS routing domains and then apply them automatically through wg-quick’s PostUp, without installing an additional DNS forwarding service.

The internal addresses in this article use a documentation-specific subnet. example.com and example.net are reserved example domains. Replace them with your own addresses and domains when deploying.

Objectives and Supported Environment

This article uses the following examples:

ItemConfiguration
WireGuard interfacewg0
DNS in the tunnel198.51.100.53
Default DNS223.5.5.5
Domains queried through the internal DNShome.arpa, example.com, example.net, and their subdomains

example.com and example.net are example domains; replace or remove them as needed. What is configured here is a domain suffix. It is not limited to top-level domains and may also be a subdomain such as corp.example.com.

This applies to Armbian, Debian, or Ubuntu systems where WireGuard is managed by wg-quick and system DNS is managed by systemd-resolved. WireGuard connections managed directly by tools such as NetworkManager do not necessarily execute these hooks, so use the configuration method for the corresponding management tool.

First, check the actual environment:

resolvectl --version
wg --version
systemctl is-active systemd-resolved
resolvectl status
ls -l /etc/resolv.conf
cat /etc/resolv.conf

If /etc/resolv.conf contains the following, the program generally accesses systemd-resolved through the local stub:

nameserver 127.0.0.53
options edns0 trust-ad
search .

Leave this file as is; do not change it to two upstream DNS servers. Multiple nameserver entries cannot themselves express domain-based splitting. This article uses the existing DNS routing capability. If the system lacks resolvectl or the service is not running, first determine how DNS is managed on the machine.

1. Set the Default DNS

Create the configuration directory and edit the file:

sudo mkdir -p /etc/systemd/resolved.conf.d
sudo vim /etc/systemd/resolved.conf.d/90-split-dns.conf

Write the following:

[Resolve]
DNS=
DNS=223.5.5.5
FallbackDNS=
Domains=
Domains=~.

The empty DNS= and Domains= clear the previously accumulated global lists before setting the values required by this article. FallbackDNS= clears the fallback list; it is not a domain-based routing rule.

~. is a routing domain that matches all domains. After more specific domain suffixes are configured for wg0 later, those suffixes will take precedence.

sudo systemctl restart systemd-resolved

This step modifies the global settings and does not clear DNS routing domains on other interfaces. If other interfaces already have ~. or identical or more specific domain rules, resolve the conflicts in their respective network management configurations first.

2. Add a DNS Hook to WireGuard

Edit the existing configuration:

sudo vim /etc/wireguard/wg0.conf

Add the following within the existing [Interface] section:

PostUp = resolvectl dns %i 198.51.100.53
PostUp = resolvectl domain %i '~home.arpa' '~example.com' '~example.net'
PostUp = resolvectl default-route %i no

Retain the existing address, key, peer, and route configurations. If there was originally a DNS = ... entry, remove it to prevent wg-quick from simultaneously setting up a separate set of DNS servers through resolvconf. Do not create a duplicate [Interface] section.

Each line serves the following purpose:

ConfigurationPurpose
resolvectl dns %i 198.51.100.53Sets the DNS server for the interface
resolvectl domain %i ...Sets the DNS routing domains handled by the interface
resolvectl default-route %i noPrevents the interface from being used as the default DNS path for unmatched domains

wg-quick replaces %i with the interface name, such as wg0. Multiple PostUp commands are executed in configuration order.

default-route no only affects DNS server selection. It does not modify the IP default route or override an explicitly configured ~. routing domain. The splitting here is primarily implemented through the global ~. and the specific suffixes on the interface.

During a normal wg-quick down, the interface is removed and the corresponding per-link DNS settings also disappear. Therefore, there is no need to add PostDown = resolvectl revert %i; after the interface has been removed, running revert may instead fail because the interface cannot be found.

The DNS server must be reachable through the tunnel. Ensure that the peer’s AllowedIPs covers 198.51.100.53, for example by already including 198.51.100.0/24. If it needs to be added, append the appropriate subnet or 198.51.100.53/32; do not overwrite existing routes. The ability to resolve DNS names does not mean that service addresses are reachable, so the corresponding routes for the service subnets are still required.

3. Apply Immediately Without Disconnecting the Tunnel

If wg0 is already running, save the file and execute:

sudo resolvectl dns wg0 198.51.100.53
sudo resolvectl domain wg0 '~home.arpa' '~example.com' '~example.net'
sudo resolvectl default-route wg0 no
sudo resolvectl flush-caches

These commands update the current runtime state, while the PostUp in the configuration file handles automatic setup the next time it starts. Running only wg syncconf does not rerun PostUp.

If the interface has not started, start it using the original management method, for example:

sudo systemctl start wg-quick@wg0

If SaveConfig = true is used, note that it may overwrite the configuration file with the runtime state when the interface is shut down. Make the appropriate changes to this option before modifying the file to avoid losing those changes.

4. Verify Split Routing

View the actual configuration:

resolvectl status

You should see 223.5.5.5 and ~. in the global settings, and 198.51.100.53 along with the three specified routing domains under wg0.

Replace the following internal domain names with records that actually exist:

resolvectl query nas.home.arpa
resolvectl query www.baidu.com

The expected rules are shown below. These are configuration targets, not actual test output from a machine:

QueryDNS Server
home.arpa, nas.home.arpa198.51.100.53
example.com, git.example.com198.51.100.53
example.net, a.b.example.net198.51.100.53
www.baidu.com223.5.5.5

To confirm the actual packets being sent, run the following in one terminal:

sudo tcpdump -ni any 'port 53 and (host 198.51.100.53 or host 223.5.5.5)'

In another terminal, clear the cache and query again. Running dig @198.51.100.53 ... directly only verifies that the DNS server is available; it cannot prove that the system’s split-routing rules are in effect.

How Routing Domains Match

~home.arpa is a routing domain used only to select DNS: it matches home.arpa itself and all of its subdomains, but it does not automatically append a suffix to short names. For example, querying nas will not automatically become nas.home.arpa for this reason.

If short-name completion is genuinely required, use the search domain home.arpa without ~. Search domains also participate in DNS routing. This article uses fully qualified domain names and consistently uses ~.

systemd-resolved gives priority to the domain with the greatest number of matching labels. Therefore, ~home.arpa is more specific than ~.. If multiple interfaces have the same best-matching domain, queries may be sent to multiple interfaces in parallel; interfaces with duplicate configurations cannot be understood as a simple primary/backup relationship.

Pass multiple domain names in a single command, separated by spaces:

sudo resolvectl domain wg0 '~home.arpa' '~example.com' '~example.net'

This command replaces the interface’s existing domain list; it does not append to it. When adding a domain, specify the complete list in the same command and update PostUp accordingly.

Limitations and Troubleshooting

  • Rules disappear after the interface is removed. Once the tunnel is disconnected, the original private domain names may instead be queried through the global DNS. This configuration is not a policy that prevents private domain names from being sent externally when the tunnel is down.
  • DNS splitting and service routing are separate. Setting the public DNS to 223.5.5.5 does not guarantee that IP traffic accessing it will use the physical interface. A full-tunnel or policy-routing setup may still route it through WireGuard.
  • Applications may bypass system DNS. A browser’s built-in DoH, a proxy application’s independent DNS configuration, or a program that explicitly specifies a DNS server may not follow these rules.
  • Proxy hijacking may still change the actual path. If sing-box TUN or firewall rules intercept port 53, inspect them together with the packet-capture results.
  • Runtime configuration may need to be reapplied. After restarting systemd-resolved alone or after another network management tool rewrites the interface DNS, check resolvectl status and, if necessary, rerun the three commands.

If resolution fails, first check the routes and service status:

ip route get 198.51.100.53
resolvectl status wg0
journalctl -u systemd-resolved -n 50 --no-pager

Also confirm the tunnel peer’s DNS listening address and ensure that the firewall permits UDP/TCP port 53 queries from clients. If an additional DNS forwarding solution was previously used, remove its conflicting configuration first, such as global DNS pointing to another local port.

References

Software versions and default network managers may vary between distributions. When deploying, verify the configuration against the local man resolvectl, man resolved.conf, and man wg-quick.

Supplement: DNS Split Routing on Debian Without WireGuard

If the internal DNS can be accessed directly through a Debian network interface, configure the routing domains on that interface. This example uses eno1.

Queried DomainUpstream DNS
home.arpa, example.com, and their subdomains192.0.2.53
Other domains192.0.2.1

Install and Configure systemd-resolved

First, run ip route get 192.0.2.53 and check the dev value in the output. Then replace eno1 below with the actual interface name.

If Debian 12 or 13 reports resolvectl: command not found, you can install systemd-resolved. The package takes over /etc/resolv.conf, so back up the current configuration first. You can skip the installation if systemd-resolved is already in use. See Debian 12 package information and Debian 13 package information.

sudo cp -L /etc/resolv.conf /etc/resolv.conf.before-resolved
sudo apt install systemd-resolved

Create /etc/systemd/resolved.conf.d/ and write the following in its 90-split-dns.conf file:

[Resolve]
DNS=
DNS=192.0.2.1
Domains=
Domains=~.

Apply the global configuration, then configure the DNS routing domains for the current interface:

sudo systemctl restart systemd-resolved
sudo resolvectl dns eno1 192.0.2.53
sudo resolvectl domain eno1 '~home.arpa' '~example.com'
sudo resolvectl default-route eno1 no

~home.arpa and ~example.com are more specific than the global ~. and therefore match first. Use resolvectl status to check whether other interfaces also have identical or more specific routing domains, and whether eno1 has only the expected internal DNS.

Persist the Configuration and Preserve IPv6

The three interface commands above modify the runtime state. If systemd-networkd is used, run networkctl status eno1 to view the Network File. For example, if the file is named 10-eno1.network, write the following in /etc/systemd/network/10-eno1.network.d/90-split-dns.conf:

[Network]
DNS=
DNS=192.0.2.53
Domains=
Domains=~home.arpa ~example.com
DNSDefaultRoute=no

[DHCPv4]
UseDNS=no
UseDomains=no

[DHCPv6]
UseDNS=no
UseDomains=no

[IPv6AcceptRA]
UseDNS=no
UseDomains=no

Run sudo networkctl reload, then use resolvectl status eno1 to confirm that the settings took effect. These settings only filter the DNS servers and domain names supplied by DHCP and IPv6 RA; IPv6 addresses and routes are still determined by the existing configuration. A DNS server on an IPv4 address can also return IPv6 AAAA records. See the systemd.network manual.

If ifupdown is used, retain the actual static address, subnet mask, and gateway in /etc/network/interfaces, and place the three post-up commands in the existing iface eno1 inet static section. The following is a complete example.

auto eno1
iface eno1 inet static
    address 192.0.2.25
    netmask 255.255.255.0
    gateway 192.0.2.1
    post-up resolvectl dns "$IFACE" 192.0.2.53
    post-up resolvectl domain "$IFACE" '~home.arpa' '~example.com'
    post-up resolvectl default-route "$IFACE" no

If an auto eno1 or allow-hotplug eno1 entry already exists, retain the original startup declaration. The global default DNS has already been set by resolved.conf.d, so remove the original dns-nameservers line from the interface section to prevent the resolvconf hook from registering the interface DNS again. gateway controls the IP default route, while resolvectl default-route controls the default DNS selection.

If other services register DNS learned through DHCP or RA with systemd-resolved, disable automatic DNS in the corresponding service or adjust its configuration, then check the list of interface DNS servers that is actually in effect.

Verification

Replace nas.home.arpa with a record that actually exists on the internal network. If the domain does not have an AAAA record, use a public query to verify IPv6 record resolution and use the internal A record to verify splitting.

resolvectl status
resolvectl query --type=A nas.home.arpa
resolvectl query --type=AAAA nas.home.arpa
resolvectl query --type=AAAA www.debian.org

If you also need to confirm the packet destination, first run sudo resolvectl flush-caches, then capture packets while querying:

sudo tcpdump -ni any 'port 53 and (host 192.0.2.53 or host 192.0.2.1)'