WireGuard split DNS on macOS: search domains and resolvers
Why your Mac sends every name to the WireGuard tunnel, how search domains differ from per-domain resolvers, and how to check what macOS really uses.
On a Mac, a WireGuard configuration whose DNS = line names a server usually sends every name
lookup to that server while the tunnel is up: the remote network’s names, and everything else.
Split DNS is the alternative. A resolver is chosen for particular domains, and every other name
keeps the DNS you normally use. Search domains are a third, separate mechanism: they complete
short names, and they do not choose a resolver. Telling the three apart explains most of the
symptoms people run into.
This guide covers macOS and IPv4 networks.
Official app status, 7 October 2026: the published WireGuard app for macOS is version 1.0.16, and it has no per-domain DNS setting. An opt-in split DNS patch has been proposed upstream, but our check found no upstream merge or published release containing it. See the official app source and macOS App Store listing.
The problem: one resolver for the whole machine
The examples below use fictional names and addresses. A remote network runs a DNS server at
10.20.0.53, the only server that knows server.corp.example.com. Your own network,
192.168.50.0/24, has names that only your router’s DNS knows: a printer, a NAS.
With one resolver for every name, one of those two sets fails. Point everything at
10.20.0.53 and your local names stop resolving, while every other lookup takes a detour
through the remote network. Keep your router’s DNS and server.corp.example.com never
resolves, even though 10.20.0.20, the address behind it, is perfectly reachable.
In the official WireGuard app
A configuration like this one is common:
[Interface]
DNS = 10.20.0.53, corp.example.com
The app’s network settings read it as follows:
10.20.0.53, an address, becomes the tunnel’s DNS server.corp.example.com, or any other entry that is not an address, becomes one of the tunnel’s search domains.- As soon as at least one server is given, the app sets the tunnel’s match-domain list to a single empty entry. The source comment reads: “All DNS queries must first go through the tunnel’s DNS.”
The published app has no setting to send only some domains to the tunnel’s DNS. The search
domain does not narrow anything: it completes names, while every query still goes to
10.20.0.53 first.
With wg-quick on macOS
wg-quick, from wireguard-tools, splits the same line into server addresses and search
domains. Its macOS script then works on the whole machine, not on a tunnel interface:
- If at least one server address is given, it sets those servers and search domains on every
network service listed by
networksetup, replacing their existing values. If the line has no search domain, it clears the services’ search domains. - While the tunnel is up, it re-applies those settings when the routing table changes.
- When the tunnel goes down, it restores the values it recorded before changing them.
- A
DNS =line with search domains but no server address changes no DNS setting at all.
This is a machine-wide DNS change, not split DNS.
Search domains are not resolver selection
What a search domain does
A search domain lets a short name be tried with a suffix: server can be looked up as
server.corp.example.com. It helps complete and search for names. It does not decide which
DNS server answers for corp.example.com.
That is consistent with the reports of search domains that “only autocomplete”: completing the name is the whole of a search domain’s job. Whichever resolver received the completed name still had to know it.
What split DNS does
Split DNS chooses a resolver for a domain. Lookups for names under corp.example.com go to
10.20.0.53; every other name stays with the resolver you normally use. The domain that
selects the resolver is often called a match domain.
A client can take its match domains and its search domains from the same list, but they remain two mechanisms. One rewrites a short name; the other chooses the server that answers for a domain. A configuration can have either one without the other.
It is not the same thing as split tunnelling
Split tunnelling decides which addresses travel through the tunnel: that is AllowedIPs.
Split DNS decides which resolver answers a name. You often need both, and they depend on each
other: the remote DNS server itself must be routed through the tunnel. In the example,
AllowedIPs has to cover 10.20.0.53 as well as 10.20.0.20. See
DNS, routing and split tunnelling for the routing half.
Your options on macOS
| Approach | What it changes | What it does not do |
|---|---|---|
| Keep the official app’s behaviour | Every lookup goes first to the tunnel’s DNS server while connected | Names that only your own network’s DNS knows may stop resolving |
| Tick “Exclude private IPs” in the official app | Routing: private IPv4 ranges leave a full tunnel | It does not choose a resolver per domain; the DNS settings stay the same |
Add a resolver file in /etc/resolver | A resolver configuration for one domain, naming the server you choose | It is not tied to the tunnel, and its interaction with the official app while connected is not established |
Use wg-quick | Machine-wide DNS while the tunnel is up, restored afterwards | No per-domain resolver; scripts can add one, and their cleanup is then yours to maintain |
| Use a client that selects resolvers per domain | Only the declared domains use the remote resolver | Check how it treats search domains, and what happens to names when the tunnel stops |
Per-domain resolvers with /etc/resolver
The manual page on your Mac, man 5 resolver, describes resolver configuration files for
individual domains, read from /etc/resolver. For a file in that directory, the file name
is used as the domain name, and the nameserver keyword gives the address of a name server
that the resolver should query. The page describes queries being routed to the
configuration whose domain best matches the name. For the example, a file named
/etc/resolver/corp.example.com would contain:
nameserver 10.20.0.53
The same page documents port and the other keywords. It also says that resolver
configurations can come from other sources, such as the System Configuration database, and
that users should make no assumptions about where the configuration comes from.
Know its limits before relying on it:
- Creating the file needs administrator rights.
- The file stays in place whether the tunnel is up or not. Nothing removes it when the tunnel
stops: it still names
10.20.0.53for that domain, a server that may only be reachable through the tunnel. - It is a macOS mechanism, not a WireGuard setting. How it interacts with the official
WireGuard app while that app is connected has not been established here: the app sends
every lookup to the tunnel’s DNS first. Do not assume one overrides the other; check the
result with
scutil --dnsand a real lookup, as described below.
What “Exclude private IPs” changes
In the official app for macOS, this checkbox appears in the tunnel editor when the
configuration has a single peer whose AllowedIPs covers every IPv4 address
(0.0.0.0/0).
Ticking it rewrites AllowedIPs:
0.0.0.0/0is replaced by a fixed list that the source describes as all non-private IPv4 addresses. It leaves out the private ranges10.0.0.0/8,172.16.0.0/12and192.168.0.0/16.- The configuration’s DNS server addresses are added, so they stay in the tunnel.
- Any other IPv4 entry in that
AllowedIPsis replaced as well.
Private addresses then leave through your normal connection. Your local network becomes reachable outside the tunnel. Remote private networks leave the tunnel too: apart from the DNS server addresses that were added, they are no longer reached through it.
The option changes routes only. The DNS settings stay as they were, and lookups still go to the tunnel’s DNS server first. It does not select a resolver per domain.
Check what macOS really uses
Three commands answer three different questions. Replace the fictional names and addresses with yours. They read the configuration or perform lookups; they change no setting.
Read the resolver configuration
scutil --dns
This answers: which resolvers has macOS been given, and for which domains? Look for the
tunnel’s DNS server, the domain attached to each resolver, the search domains, and the
interface each resolver is scoped to. A resolver with no domain and a resolver for
corp.example.com play different roles. The first resolver listed is not necessarily the one
used for every query.
scutil --dns shows configuration, not the result of a lookup.
Resolve through the system
dscacheutil -q host -a name server.corp.example.com
This answers: what does macOS return when it is asked for this name through its standard
lookup calls? Its manual page, man dscacheutil, says it initiates a query using standard
calls, and that the result can come from a cache. It shows the address the system returns,
not which DNS server provided it. Use the fully qualified name, and compare the returned
address with the one you expect.
Query one server directly
dig @10.20.0.53 server.corp.example.com
This answers: does that server know the name, and can you reach it? dig @server sends the
query straight to the server you name and bypasses the system’s resolver selection. A
correct answer proves that the server works and is reachable. It does not prove that macOS
will choose that server for the name.
Test with the real application
Open the service by its full name, for instance https://server.corp.example.com/. This
also checks the HTTPS certificate, which only validates on the real hostname. An
application that uses its own DNS mechanism can behave differently from the system resolver.
Cached answers can hide a change: after changing a setting, re-test with names whose
expected answer you know.
Common traps
| Symptom | What to check |
|---|---|
| Only the short name fails | The search domain: absent, or not applied by your client. Test the full name |
| The full name fails, the address answers | The resolvers and their domains in scutil --dns; what the system returns with dscacheutil |
| Every name goes through the tunnel | The official app’s catch-all DNS, or wg-quick’s machine-wide settings |
| Local names stop resolving once connected | A remote resolver answering for everything; see the options above |
dig @10.20.0.53 answers, the browser does not | dig bypassed the system’s resolver selection; read scutil --dns, and compare with what dscacheutil returns |
| No answer at all from the remote DNS server | Its address in AllowedIPs; see DNS, routing and split tunnelling |
| Names fail after the tunnel stops | Settings not restored, or a file left in /etc/resolver |
A name ending in .local does not resolve through DNS | Bonjour’s multicast DNS uses that suffix, and Apple has documented resolution failures for unicast DNS names ending in .local |
If nothing answers even by address, the problem is the tunnel itself: see When the tunnel will not connect when no handshake completes, and WireGuard connected but no traffic when one does.
Why it matters once several networks are connected
With two remote networks that each have their own DNS server, a single resolver is wrong for one of them: neither server knows the other network’s names. Several VPN clients that each change the machine’s DNS settings can also interfere with one another, particularly when one of them stops. For two WireGuard tunnels at once, routes and DNS together, see Multiple WireGuard tunnels on macOS.
Using Paart for split DNS
Paart Client is one WireGuard client that selects a resolver per domain. Its split DNS works with internal names that you declare for each connection.
What internal names do
Nothing is split until you say which names belong to the remote network. In the
connection’s detail, open Names and choose Add an internal name: int.example.com,
say. Those names, and only those, are resolved through the tunnel. Every other name keeps
this device’s DNS:
int.example.comgoes through the tunnel to the remote resolver,10.0.99.10;google.comgoes to the DNS of the Wi-Fi you are on.
Unless you pick an override resolver in Settings › Resolvers: that resolver then answers every other name instead.
The Look up with row, at the top of Names, says where the internal names are looked up. Left empty, it is the configuration’s DNS. With no internal name listed, nothing is split: the resolver chosen under DNS answers every name. On macOS, Paart attaches the internal names to the chosen resolver as match domains.
Internal names select a resolver; they are not search domains. Paart does not apply the
search domains of a configuration’s DNS = line: they are dropped on import, and the import
says so. Use fully qualified names, such as server.int.example.com.
Internal names are available on Mac, iPhone and iPad. They are part of advanced networking: on the free tier they work on your Free connection, and on the others they come with Paart Pro.
Technical sources
- wireguard-apple configuration parser
— how the official client splits
DNS =into servers and search domains. - wireguard-apple network settings — DNS servers, search domains and the catch-all match domain in the official client.
- wireguard-apple tunnel model
— what “Exclude private IPs” does to
AllowedIPs. - wg-quick reference
and Darwin source
— how
wg-quicksets and restores DNS on macOS. The Darwin source is the platform-specific reference. man 5 resolverandman dscacheutil, on your Mac — per-domain resolver files in/etc/resolver, and lookups through the system’s standard calls.- Apple Support: domains that end in .local — Bonjour’s use of the suffix, and unicast DNS names that may fail to resolve. Apple has archived this article.
- wireguard-apple history and patch proposal including opt-in split DNS — distinguish a proposal from an upstream merge and a released app.
Source review: 7 October 2026. The official app and wg-quick are described from their
upstream source, not from a test of every macOS version.