Module 06 · 20 min read

MagicDNS and split DNS

How Tailscale's Quad100 resolver, MagicDNS names, per-platform OS resolver rewiring, split DNS, and exit node DNS behavior actually work, and how they fail.

Where this module sits in the curriculum 00 01 02 03 04 05 06 07 08 09 10 11 12 06 MagicDNS and split DNS

The promise

  1. You will be able to explain what Quad100 (100.100.100.100) is, why it never leaves your device, and why every DNS feature Tailscale ships is implemented inside it.
  2. You will be able to decode any MagicDNS name into its parts (machine name, tailnet name, ts.net suffix) and predict when a short name will and will not resolve.
  3. You will be able to describe, per platform, exactly how Tailscale rewires the OS resolver: systemd-resolved and resolv.conf on Linux, scoped resolvers on macOS, NRPT rules on Windows.
  4. You will be able to state precisely what “Override DNS servers” does and does not do, and when split DNS (restricted nameservers) is the better tool.
  5. You will be able to predict how DNS behaves when an exit node is in use, and which admin console setting changes that.
  6. You will be able to run a five minute DNS diagnosis with dig @100.100.100.100, tailscale dns status, and the platform native lookup tools, and localize the fault to one of four layers.

Foundation

You already know how a stub resolver works: applications call the OS resolver library, the OS consults its configured nameservers, and a recursive resolver somewhere does the real work. You know that /etc/resolv.conf is the classic Unix expression of that configuration, that search domains let ping db1 expand to ping db1.corp.example, and that the DNS client on every OS has grown layers of caching, policy, and interface scoping that the classic model does not capture.

You also know the pain: every VPN ever built has to answer the question “who resolves names while the tunnel is up?” Full tunnel VPNs traditionally seize the whole resolver configuration. That works until the user also needs their home printer, their corporate intranet, and a second VPN at the same time. The industry answer to this is split DNS: route queries for certain domains to certain resolvers, leave everything else alone.

Tailscale’s DNS story is a specific, opinionated implementation of that answer. The core move: instead of pointing your OS at some remote resolver, Tailscale points it at a resolver that lives inside the daemon already running on your machine. Everything else follows from that one decision.

One more piece of context from earlier modules: the control plane (Module 02) is how DNS configuration reaches your device. MagicDNS name mappings, global nameservers, restricted nameservers, and search domains are all tailnet wide settings distributed by the coordination server, then enforced locally by each node. DNS resolution itself never touches Tailscale’s infrastructure.

Core content

Quad100: the resolver that lives in the daemon

The analogy: Quad100 is the concierge desk in the lobby of your own building. You never leave the building to ask it a question. If you ask about a resident (a tailnet machine), the concierge answers from the building’s own directory, instantly, with no outside call. If you ask about anything else, the concierge picks up the phone and calls the right outside party on your behalf, following whatever calling rules management has posted.

The mechanism: 100.100.100.100 is a reserved address inside the Tailscale CGNAT range that behaves like a localhost service. Traffic to it is handled by tailscaled (or the equivalent client process) on your own device and does not leave the machine unless the service being provided requires forwarding. There is an IPv6 twin, fd7a:115c:a1e0::53. On port 53, Quad100 runs a stub DNS resolver with these jobs:

  1. Resolve MagicDNS names for your tailnet locally, from the netmap the control plane already pushed to you. No network round trip, no external logging.
  2. Forward every other query upstream according to tailnet DNS policy: to restricted nameservers for matching domains, to global nameservers if configured, or to the resolvers the OS already had.
  3. Use DNS-over-HTTPS to upstream resolvers that support it, so forwarded queries are encrypted in transit where possible. Tailscale applies DoH automatically for known public providers such as Cloudflare and Google.
  4. When the device is using an exit node, forward queries through the tunnel to the exit node instead (more on this below).

Quad100 also serves a device management web interface on port 80, enabled by default in Tailscale v1.64.0 and later (checked 2026-08-10), but that is a management surface, not part of the DNS path.

The failure mode: because the OS resolver is pointed at Quad100, the daemon is now load bearing for all DNS on the machine, not just tailnet DNS. If tailscaled hangs, crashes, or is stopped without cleaning up the resolver configuration, the symptom is not “Tailscale is down.” The symptom is “the internet is down,” because every DNS query on the box is being sent to a resolver that no longer answers. This single fact explains a large fraction of “Tailscale broke my laptop’s WiFi” reports: WiFi was fine, DNS was orphaned.

MagicDNS names: the naming scheme

The analogy: MagicDNS is a phone contact list that writes itself. Every device that joins the tailnet gets an entry automatically, the entry follows the device across networks and IP changes, and everyone in the tailnet sees the same list.

The mechanism: every device gets a fully qualified domain name built from two parts:

machine-name . tailnet-name . ts.net

For example node-a.velvet-osprey.ts.net. The machine name is generated from the OS hostname when the device joins; you can edit it in the admin console, and if the device’s name changes, the MagicDNS entry changes with it. The tailnet name is assigned as tail<ID>.ts.net using a random hexadecimal string (something like tailfe8c.ts.net), and admins can replace it with a randomly generated memorable name like cat-crocodile.ts.net from the DNS page of the admin console. You pick from generated options and can re-roll for more; you cannot type an arbitrary name. You can revert to the original hex name later.

Short names come from search domains. When MagicDNS is enabled, Tailscale automatically adds your tailnet domain as a search domain on each device. That is the entire trick behind ssh node-a working: the OS expands node-a to node-a.velvet-osprey.ts.net before resolving. MagicDNS is enabled by default for tailnets created on or after October 20, 2022, and since Tailscale v1.20 it no longer requires you to configure a global nameserver first (checked 2026-08-10).

The failure mode, in three flavors. First, renaming: the tailnet name is embedded in every FQDN and every HTTPS certificate (Module 08), so renaming the tailnet can break existing links, scripts, and pinned names; also, once a memorable name has been used for HTTPS certificates, that specific name cannot be generated again after you release it. Second, staleness: MagicDNS answers come from the local netmap, so a device that has lost its control plane connection can keep resolving from a stale map or fail to learn about new peers. Third, collisions: a machine name that shadows a real host in another search domain resolves through whichever search domain the OS applies to the query, and since Tailscale adds the tailnet domain to the device’s search list, people whose LAN also has a node-a get surprised.

The legacy .beta.tailscale.net domain stopped working on September 13, 2024. If you find it in an old script, that script has been broken for a while.

How the OS resolver gets rewired, per platform

This is the part that makes or breaks real deployments, because Tailscale does not get to invent a DNS API. It has to use whatever each OS provides, and the three major desktop platforms provide three completely different things.

The analogy: Tailscale is a new tenant who needs their mail routed correctly in three different countries, each with a different postal bureaucracy. Same goal everywhere, three different sets of forms.

How each OS routes DNS queries to Quad100 Linux app query systemd-resolved stub or rewritten resolv.conf nameserver 100.100.100.100 macOS app query system resolver scoped resolver entries per domain, via extension Windows app query DNS Client service NRPT policy rules match ts.net and split domains Quad100 resolver in tailscaled 100.100.100.100:53

Linux. There is no single Linux DNS system, so Tailscale detects what is managing /etc/resolv.conf and cooperates with it. The preferred setup is systemd-resolved: Tailscale registers its resolver and its domains with resolved, resolved keeps its stub on 127.0.0.53, and /etc/resolv.conf should be a symlink to /run/systemd/resolve/stub-resolv.conf. In that world, real split DNS works: resolved sends ts.net (and any restricted domains) to Quad100 and everything else wherever it was already going. If nothing is managing resolv.conf, Tailscale falls back to rewriting the file directly, placing 100.100.100.100 as the nameserver. That works, but it is a shared file with no locking: dhclient, NetworkManager, and cloud-init all believe they own it too, and the last writer wins. Tailscale’s own Linux DNS documentation is frank that this is a messy space and recommends centralizing on systemd-resolved or resolvconf so DHCP clients stop fighting over the file.

macOS. The Tailscale client runs as a Network Extension and installs scoped resolver entries: the system resolver is told “for these domains, use this resolver” rather than having its primary resolver replaced. You can see these entries with scutil --dns. The critical consequence is that only lookups going through the system resolution APIs honor scoped resolvers. The classic BSD tools host, nslookup, and dig speak DNS directly to whatever server they choose and bypass the system resolver entirely, so on macOS they will fail to resolve MagicDNS names even while Safari and ssh resolve them fine. The supported way to test what the OS actually does is dscacheutil -q host -a name node-a.velvet-osprey.ts.net.

Windows. Tailscale uses the Name Resolution Policy Table, NRPT, a Windows policy mechanism that says “queries matching this namespace go to this DNS server.” Tailscale installs NRPT rules for the ts.net domain and for any restricted nameserver domains, steering those to Quad100 while the interface’s normal DNS servers keep handling the rest. Same trap as macOS, different reason: nslookup does not honor NRPT rules, so it lies to you about split DNS behavior. Use PowerShell’s Resolve-DnsName, which goes through the DNS Client service and honors policy.

The failure mode common to all three platforms: partial teardown. DNS configuration is stateful OS mutation, and anything that kills the client without letting it clean up (force quit, crash, aggressive scripts) can leave the OS pointing at a resolver that is gone. On Linux this looks like a stale resolv.conf; on Windows, stale NRPT rules; on macOS, stale scoped entries. All three present as “no DNS until I toggled Tailscale or rebooted.”

Every platform has an opt out. tailscale set --accept-dns=false on Linux, unchecking “Use Tailscale DNS settings” on macOS, and deselecting “Use Tailscale DNS” from the tray menu on Windows all tell the client: keep the tunnel, leave my resolver alone. With DNS acceptance off, MagicDNS names stop resolving through the OS (you can still query Quad100 explicitly), which makes this both a useful escape hatch and a common self inflicted mystery.

“Override DNS servers”: what it really does

The analogy: by default, Tailscale’s DNS settings are a set of additions to the phone book your device already had. Override local DNS replaces the phone book.

The mechanism: in the admin console DNS page, global nameservers are resolvers for all domains. Without the override toggle, devices may still use their locally configured (DHCP provided) resolvers alongside tailnet policy. With “Override DNS servers” enabled, devices ignore their local DNS configuration and use only the tailnet’s global nameservers for general resolution. Queries still enter through Quad100; the override controls what Quad100 forwards to. This is how you guarantee every device resolves through, say, a filtering resolver or an internal DNS server, no matter what network it sits on. One detail worth knowing: when you add a known public provider, Tailscale treats that provider’s address list as a unit, so adding one of its addresses brings the set.

The failure mode: override is an availability bet. The moment you enable it, every device’s DNS depends on being able to reach the configured nameservers. A device on a captive portal network, or one whose only route to an internal nameserver is a subnet router that just went down (Module 07), now has no DNS at all, because you told it to ignore the local resolvers that do work. Tailscale’s guidance is to make sure all devices can reach the global nameservers before forcing them to use tailnet DNS settings, and best practice is more than one global nameserver for redundancy.

Split DNS: restricted nameservers

The analogy: split DNS is a mail sorting rule. Letters addressed to corp.example go in the courier bag for headquarters; everything else goes in the ordinary mail.

The mechanism: a restricted nameserver is a resolver paired with a domain. Configure 10.0.0.53 for corp.example and Quad100 forwards any query matching *.corp.example to 10.0.0.53, touching nothing else. This composes cleanly with everything above: MagicDNS handles ts.net, restricted nameservers handle your named internal domains, and global or local resolvers handle the public internet. It is the tool for “my AWS VPC has private zones,” “my office has an AD domain,” and “I need internal names only when they are internal names.”

Two behavioral notes worth engraving. First, ordering: when you list multiple resolvers, modern OS resolver stacks may query them in parallel or reorder them by performance, and Tailscale explicitly does not guarantee that resolvers are consulted in the order configured. If order matters to you, that is the signal that you actually want split DNS, where domain matching replaces ordering. Second, search domains: you can add tailnet wide search domains so short internal names expand for everyone, usable on devices running Tailscale v1.34 and later.

Quad100 query decision flow query arrives at Quad100 name in tailnet? (ts.net) yes answer locally from netmap, no network no matches restricted domain? yes forward to that nameserver only no global nameservers set? yes forward to global (only these if override on) no forward to the resolvers the OS already had

The failure mode: split DNS quietly depends on reachability of the restricted nameserver, which is often itself behind Tailscale (a subnet router or an internal node). When that path breaks, only names under the restricted domain die, which produces the classic confusing symptom: “the internet is fine, ts.net names are fine, but everything under corp.example times out.” That pattern is a routing problem wearing a DNS costume; check Module 07 before you blame the resolver.

Exit nodes and DNS

The analogy: when you use an exit node, you have moved into someone else’s building, so by default you use their concierge for outside calls too.

The mechanism: when a device routes through an exit node (Module 07), Quad100 forwards DNS queries through the tunnel to be resolved via the exit node, regardless of the nameservers you configured. This is deliberate privacy behavior: if all your traffic exits from cloud-1, your DNS should too, or your queries leak your browsing to the local network while your traffic exits elsewhere. Since it is a Quad100 behavior, it applies to forwarded queries; MagicDNS names still answer locally from the netmap.

The control: in the admin console, each nameserver has a “Use with exit node” setting. Enabling it says: even when an exit node is active, keep sending matching queries to this nameserver. That is the tool for “laptops use exit nodes for privacy, but corp.example must still resolve against the internal resolver.”

The failure mode: an exit node whose own DNS is broken breaks DNS for every client currently using it, and the clients’ own perfectly healthy resolvers do not save them. Symptom signature: names fail only while the exit node is selected, tailscale ping to peers still works, and deselecting the exit node instantly fixes resolution.

DNS query path with an active exit node app on node-a local Quad100 on node-a exit node cloud-1 via WireGuard tunnel upstream resolver non-tailnet query ts.net query: answered locally, never forwarded exception: nameservers marked "Use with exit node" still receive their matching queries directly

On the wire

DNS problems are the easiest Tailscale problems to localize, because you can interrogate each layer separately. The layers, from the inside out: the Quad100 resolver, the tailnet DNS policy it received, the OS resolver plumbing pointing at it, and the upstreams it forwards to.

Ask Quad100 directly, bypassing all OS plumbing:

$ dig +short node-b.velvet-osprey.ts.net @100.100.100.100
100.101.102.103

If this answers, the daemon is up, MagicDNS is on, and the name exists in your netmap. Everything left to debug is OS plumbing or upstream policy. If it times out, stop debugging the OS: the daemon itself is not serving.

Ask the daemon what DNS configuration it thinks it has:

$ tailscale dns status
=== 'Use Tailscale DNS' status ===
Tailscale DNS: enabled.
...
=== MagicDNS configuration ===
MagicDNS: enabled tailnet-wide (suffix = velvet-osprey.ts.net)
Other devices in your tailnet can reach this device at node-a.velvet-osprey.ts.net
Search Domains:
  - velvet-osprey.ts.net
Routes:
  - corp.example -> 10.0.0.53:53

tailscale dns status prints the local forwarder configuration and the tailnet wide MagicDNS configuration; add --all for advanced debugging detail. Its sibling tailscale dns query name type, available in Tailscale v1.76.0 and later (checked 2026-08-10), performs a query through the local forwarder itself, which tests exactly the path applications use minus the OS resolver layer.

Then test the OS layer with the tool that honors it:

# macOS: uses the system resolver, honors scoped resolvers
$ dscacheutil -q host -a name node-b.velvet-osprey.ts.net
name: node-b.velvet-osprey.ts.net
ip_address: 100.101.102.103

# Windows: honors NRPT, unlike nslookup
PS> Resolve-DnsName node-b.velvet-osprey.ts.net

# Linux with systemd-resolved: show who owns which domain
$ resolvectl status tailscale0
Link 5 (tailscale0)
    Current DNS Server: 100.100.100.100
           DNS Servers: 100.100.100.100
            DNS Domain: velvet-osprey.ts.net corp.example ~0

One caution on interpreting captures and logs: because modern resolver stacks parallelize and reorder upstreams, do not read meaning into which of several global nameservers answered a given query. Tailscale documents that configured resolver order is not guaranteed.

Failure modes

  1. Everything resolves by IP, nothing by name, on one machine. tailscale ping node-b works, dig @100.100.100.100 times out. The daemon’s resolver is not serving: client stopped, hung, or MagicDNS disabled tailnet wide. Restart the client; check the admin console DNS page.
  2. ts.net names dead, internet fine. dig @100.100.100.100 answers correctly but normal lookups fail. The OS plumbing layer is broken: DNS acceptance turned off (--accept-dns=false, the macOS checkbox, the Windows tray toggle), or another program reclaimed the resolver configuration. On Linux, read /etc/resolv.conf and ask what wrote it last.
  3. Names resolve on macOS but “not” on the same Mac in a terminal. ssh node-b works, host node-b fails. Not a failure at all: host, nslookup, and bare dig bypass the macOS system resolver and its scoped entries. Test with dscacheutil before believing anything is wrong.
  4. Names resolve everywhere except one Windows box, but only in nslookup. Mirror image of the macOS trap: nslookup ignores NRPT, so it reports failure (or wrong answers) for names that every real application resolves fine. Use Resolve-DnsName.
  5. No DNS at all after Tailscale crashed or was force killed. The OS still points at a Quad100 nobody is answering. Symptom is total resolution failure that survives until the client restarts and rewrites, or you manually restore, the resolver configuration. This is the partial teardown failure.
  6. DNS dies periodically on a Linux server, then comes back. Classic resolv.conf fight: dhclient or NetworkManager rewrites the file on lease renewal, clobbering Tailscale’s entry, and one side eventually rewrites it back. Fix by centralizing on systemd-resolved with /etc/resolv.conf symlinked to the stub file. On Amazon Linux 2023, check for the documented forwarding loop where a backup resolv.conf feeds 100.100.100.100 back to itself.
  7. Internal domain dead, everything else fine. corp.example names time out while ts.net and public names work. The restricted nameserver for that domain is unreachable, usually because the route to it (often a subnet router) is down. This is Module 07’s problem surfacing as DNS.
  8. DNS breaks only when an exit node is selected. Forwarded queries are resolved via the exit node by default; if the exit node’s DNS path is broken, yours is too. Deselect the exit node to confirm, then fix the exit node, or mark critical nameservers “Use with exit node.”
  9. Enabling “Override DNS servers” took a fleet segment offline. Devices that cannot reach the tailnet’s global nameservers (captive portals, isolated networks, a down subnet router in front of an internal resolver) lose all DNS, because you told them to ignore the local resolvers that still worked. Verify reachability from every network segment before enabling override, and configure redundant nameservers.
  10. Another VPN and Tailscale coexist badly. A full tunnel corporate VPN that seizes the whole resolver, or a second product writing its own scoped entries or NRPT rules, can shadow Tailscale’s configuration (or be shadowed by it). Symptom: name resolution depends on which client connected last. Durable fix: prefer split DNS scoping on both sides so each owns only its domains.
  11. Short names fail, FQDNs work. Search domain missing on that device (DNS acceptance off), or the target is a machine shared from another tailnet, which never gets a short name and requires its full ts.net FQDN.
  12. Scripts broke after the tailnet was renamed. Every FQDN changed with the tailnet name. Old links, pinned certs, and hardcoded names break by design; and a memorable name once used for HTTPS certificates cannot be generated again later.

Check yourself

1. A user reports: “Tailscale DNS is broken on my Mac. nslookup node-b fails, but weirdly the browser can open http://node-b just fine.” What is happening, and what single command settles it?

Answer: Almost certainly nothing is broken. On macOS, Tailscale configures DNS through scoped resolver entries attached to the system resolver, and only lookups made through the system resolution APIs see those entries. The browser and ssh use those APIs, so node-b expands via the MagicDNS search domain and resolves through Quad100. But nslookup, host, and bare dig construct DNS queries themselves and send them to the default resolver directly, bypassing the scoped entries entirely, so they fail on MagicDNS names by design. The command that settles it is dscacheutil -q host -a name node-b.velvet-osprey.ts.net, which exercises the real system resolver path; if that returns the 100.x address, the system is healthy and the only bug was the test methodology. If you want a resolver tool result anyway, dig node-b.velvet-osprey.ts.net @100.100.100.100 targets Quad100 explicitly and works on every platform.

2. You enable “Override DNS servers” with a single global nameserver, 10.0.0.53, which lives in the office and is advertised over a subnet router. Remote laptops immediately lose all DNS whenever the office subnet router reboots. Explain the full causal chain and two distinct fixes.

Answer: With override enabled, every device ignores its locally configured resolvers and forwards all non-tailnet queries through Quad100 exclusively to the tailnet’s global nameservers, here a single internal resolver reachable only via the subnet router. When the subnet router goes down, laptops lose the route to 10.0.0.53; Quad100 has no other upstream and is forbidden from falling back to local resolvers, so every public lookup times out. MagicDNS names keep working, which is a diagnostic tell. Fix one: add at least one redundant, independently reachable global nameserver (a second internal resolver on a different path, or a public resolver if policy allows), following the documented best practice of multiple global nameservers. Fix two: stop using override for this goal entirely; make 10.0.0.53 a restricted nameserver for the internal domain (split DNS), so only corp.example queries depend on the office path and everything else uses whatever resolver the laptop’s network provides. Choose fix two when the real requirement was “resolve internal names,” and reserve override for when you genuinely must force all resolution through controlled resolvers.

3. A laptop using exit node cloud-1 resolves public names fine, but the Customer’s internal zone corp.example, served by a restricted nameserver at the Customer site, stopped resolving the moment the exit node was selected. Peers still ping. What is the mechanism, and what setting addresses it?

Answer: By default, once an exit node is active, Quad100 forwards DNS queries via the exit node regardless of the tailnet’s configured nameservers. Public resolution works because cloud-1’s resolution path is healthy, but queries for corp.example are now being resolved from cloud-1’s vantage point instead of being forwarded from the laptop to the restricted nameserver, and cloud-1 either has no route to the internal resolver or no knowledge of the zone, so those lookups die. Peer pings still work because tailnet connectivity and MagicDNS are unaffected. The addressing setting is per nameserver in the admin console: enable “Use with exit node” on the restricted nameserver for corp.example, which tells clients to keep sending matching queries to that nameserver even while an exit node is active. Then verify the laptop can actually reach that nameserver’s route while the exit node is engaged, since split DNS still depends on reachability.

What you now have

  1. A mental model of Quad100 as a per-device, in-daemon stub resolver at 100.100.100.100 (and fd7a:115c:a1e0::53): MagicDNS answered locally from the netmap, everything else forwarded by policy, DoH used upstream where available.
  2. The anatomy of a MagicDNS name, machine plus tailnet plus ts.net, why short names are just a search domain trick, and why renaming a tailnet is a breaking change.
  3. The three platform plumbing stories: systemd-resolved or resolv.conf on Linux, scoped resolvers on macOS, NRPT on Windows, and which diagnostic tool tells the truth on each.
  4. The real semantics of global nameservers, “Override DNS servers,” restricted nameservers (split DNS), and search domains, including the no-guaranteed-ordering rule.
  5. The exit node DNS default (resolve via the exit node) and the per nameserver “Use with exit node” escape hatch.
  6. A layered diagnostic method: dig @100.100.100.100, then tailscale dns status and tailscale dns query, then the platform native resolver test, each isolating one layer.

Cross references

Sources

Every factual claim in this module traces to one of these, checked on the date shown. The full ledger lives at /sources.

  1. MagicDNS checked 2026-08-10
  2. DNS in Tailscale (DNS settings) checked 2026-08-10
  3. What is 100.100.100.100? checked 2026-08-10
  4. Tailnet name checked 2026-08-10
  5. Configuring Linux DNS checked 2026-08-10
  6. DNS in Tailscale (reference) checked 2026-08-10
  7. Tailscale CLI checked 2026-08-10