macOS 26 breaks /etc/resolver/ supplemental DNS for custom TLDs · GitHub

🚀 Read this awesome post from Hacker News 📖

📂 **Category**:

💡 **What You’ll Learn**:

Ah, the joys of waking up to find the Mac’s done an overnight upgrade… and erm, suddenly things stop working. Thankfully, me and Claude managed to work out what the fuck is going on… I’m sharing here, as well as having raised in on https://feedbackassistant.apple.com/feedback/22280434 (that seems to need a login?). # Bug Report: macOS 26 breaks /etc/resolver/ supplemental DNS for custom TLDs **Product:** macOS 26.3.1 (Darwin 25.3.0, Build 25D771280a) **Component:** Networking → DNS / mDNSResponder **Regression from:** macOS 25.x (working immediately prior to overnight update) — ## Summary The `/etc/resolver/` per-domain DNS resolver mechanism — an Apple-documented, long-standing macOS feature — is silently broken in macOS 26 for any TLD that is not present in the IANA root zone. `mDNSResponder` intercepts queries for custom/private TLDs and handles them as mDNS (multicast DNS), never consulting the unicast nameserver specified in the resolver file. This breaks an entire class of local development and private network DNS workflows that previously worked correctly on macOS 25 and earlier. — ## Background macOS supports per-domain DNS resolver configuration via files placed in `/etc/resolver/`. A file named `/etc/resolver/internal` containing `nameserver 127.0.0.1` instructs the DNS stack to send all `*.internal` queries to the local nameserver at 127.0.0.1. This mechanism is documented in `man 5 resolver` and has worked reliably since at least macOS 10.6. It is widely used by developers running local DNS servers (dnsmasq, bind, unbound) to resolve private domain suffixes. This machine runs `dnsmasq` (via Homebrew) as a local DNS resolver, configured to answer queries for `*.internal` domains (static entries for a local web application) and forward everything else upstream. The `/etc/resolver/internal` file routes these queries to dnsmasq. This setup worked correctly on macOS 25.x. — ## Steps to Reproduce 1. Install dnsmasq and configure it to answer a custom domain: “` # /opt/homebrew/etc/dnsmasq.d/test.conf address=/probe.example-private/127.0.0.1 “` 2. Start dnsmasq: `brew services start dnsmasq` 3. Verify dnsmasq answers directly: “` dig @127.0.0.1 probe.example-private A +short # Returns: 127.0.0.1 ✓ “` 4. Create a resolver file: “` sudo sh -c ‘echo “nameserver 127.0.0.1” > /etc/resolver/example-private’ “` 5. Flush DNS cache and restart mDNSResponder: “` sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder “` 6. Verify `scutil –dns` shows the resolver is registered: “` scutil –dns | grep -A4 “example-private” # Shows: domain: example-private, nameserver: 127.0.0.1 ✓ “` 7. Attempt to resolve via the system resolver: “` ping -c1 probe.example-private # ping: cannot resolve probe.example-private: Unknown host ✗ python3 -c “import socket; print(socket.getaddrinfo(‘probe.example-private’, 80))” # socket.gaierror: [Errno 8] nodename nor servname provided, or not known ✗ “` — ## Expected Behaviour `ping`, `curl`, and any application using `getaddrinfo()` should resolve `probe.example-private` to `127.0.0.1`, as specified by the dnsmasq `address=` directive, reached via the `/etc/resolver/example-private` unicast nameserver entry. This is exactly what happened on macOS 25.x. — ## Actual Behaviour All resolution via `getaddrinfo()` (i.e. every real application — browsers, curl, ping) fails with “Unknown host”. No DNS traffic reaches dnsmasq. Instead, `mDNSResponder` intercepts the query and immediately returns a cached “No Such Record” mDNS response with an anomalously large TTL (~108002 seconds). Evidence from `dns-sd -G v4 probe.example-private`: “` Timestamp A/R Flags IF Hostname Address TTL 11:42:03.617 Add 40000002 0 probe.example-private. 0.0.0.0 108002 No Such Record “` Evidence from `tcpdump -i lo0 -n port 53` captured during a `getaddrinfo()` call: “` 0 packets captured “` No packets reach dnsmasq on 127.0.0.1:53 at all. mDNSResponder handles the query entirely internally via mDNS and never consults the unicast nameserver. — ## Scope Tested TLDs that fail: | TLD | Status | Notes | |—–|——–|——-| | `.internal` | Broken | IETF draft special-use TLD; worked on macOS 25 | | `.test` | Broken | RFC 6761 §6.2 — explicitly reserved for local testing | | `.home.arpa` | Broken | RFC 8375 — IANA reserved for residential private networks | | `.lan` | Broken | Widely used convention (not IANA reserved, but irrelevant) | | Arbitrary (e.g. `.emflocal`) | Broken | Any TLD not in the IANA root zone | **`.test` is particularly egregious**: RFC 6761 Section 6.2 explicitly reserves `.test` for exactly this use case — local/private DNS testing — and specifies that resolvers SHOULD resolve it via normal DNS mechanisms. macOS 26 silently overrides this by treating it as mDNS-only. `google.com`, `bbc.co.uk` and other standard IANA-registered TLDs continue to work correctly via the default unicast resolver. Only custom/unregistered/special-use TLDs are affected. — ## Workaround The only reliable workaround is to add entries manually to `/etc/hosts`, which bypasses mDNSResponder entirely. This is impractical for dynamic use cases (e.g. Docker container DNS, where host entries change frequently) and requires sudo for every change. — ## Impact This breaks the standard local development DNS workflow that has been documented and recommended by the macOS developer community for over a decade: – Any developer using dnsmasq + `/etc/resolver/` for `*.test`, `*.local`, `*.internal`, or other private TLDs – Docker Desktop’s (and similar tools’) container name resolution via custom TLDs – Any tool that generates `/etc/resolver/` entries as part of its macOS integration (e.g. Vagrant, Tailscale, various VPN clients) – Kubernetes local development tools (minikube, kind, k3d) that use `*.cluster.local` or similar The failure is silent: `scutil –dns` correctly shows the resolver configuration is registered, leading users to believe the setup is correct while resolution silently fails. There is no log output, no error, and no indication that mDNS interception is occurring. — ## Environment – **macOS version:** 26.3.1 (ProductVersionExtra: (a)) – **Build:** 25D771280a – **Hardware:** Apple Silicon (arm64) – **Regression:** Working on macOS 25.x immediately before overnight system update – **dnsmasq version:** Homebrew, listening on 127.0.0.1:53 – **Verified via:** `dig @127.0.0.1` (works), `host` (works — uses own resolver), `ping`/`curl`/`python3 socket.getaddrinfo` (all fail) — ## References – `man 5 resolver` (macOS) — documents the `/etc/resolver/` mechanism – RFC 6761 — Special-Use Domain Names (reserves `.test`, `.localhost`, `.invalid`, `.example`) – RFC 8375 — Special-Use Domain `home.arpa` – IETF draft-ietf-dnsop-interneti-mdn — `.internal` special-use proposal

💬 **What’s your take?**
Share your thoughts in the comments below!

#️⃣ **#macOS #breaks #etcresolver #supplemental #DNS #custom #TLDs #GitHub**

🕒 **Posted on**: 1773935617

🌟 **Want more?** Click here for more info! 🌟

By

Leave a Reply

Your email address will not be published. Required fields are marked *