skip to content

A colleague adds a nameserver line to /etc/resolv.conf on a Mac and reports that DNS resolution is unchanged for their apps. Why does that happen on macOS, and how do you actually change and inspect the system's DNS configuration?

level: middleimportance: should knowfreq 42%

answer

  1. that file is generated, not read
  2. configuration lives in a live store
  3. resolvers are per service, and scoped
  4. networksetup sets, scutil inspects
  5. dig bypasses the system resolver

basics

~20 s

On macOS /etc/resolv.conf is generated from the SystemConfiguration store, not read as the source of truth; the system resolver goes through mDNSResponder using per-interface settings. Change DNS with networksetup -setdnsservers (or the network settings UI) and inspect the real configuration with scutil --dns.

solid answer

~40 s

macOS keeps network configuration in the **SystemConfiguration dynamic store**, maintained by `configd`. `/etc/resolv.conf` is a symlink to a file that `configd` *generates* from that store for the benefit of legacy code linked against the BIND resolver; it is an output, not an input. Applications resolve names through `mDNSResponder`, which uses the dynamic store's per-interface and per-domain resolver configuration, so a hand-written `nameserver` line is either overwritten or simply not consulted. To change DNS for a network service you use `networksetup -setdnsservers Wi-Fi 1.1.1.1 8.8.8.8` (or `Empty` to hand control back to DHCP), or a configuration profile on a managed Mac. To inspect it, `scutil --dns` prints every resolver, in order, with its search domains and the interfaces it is scoped to. `dscacheutil -flushcache; sudo killall -HUP mDNSResponder` clears the cache.

code

bash · 15 lines
bash
# What services exist, and what DNS is configured for one of them
networksetup -listallnetworkservices
networksetup -getdnsservers Wi-Fi

# Set, then hand control back to DHCP
sudo networksetup -setdnsservers Wi-Fi 1.1.1.1 8.8.8.8
sudo networksetup -setdnsservers Wi-Fi Empty

# What the system will really do with a lookup, including scoped resolvers
scutil --dns

# Resolve through the same path applications use, then flush the cache
dscacheutil -q host -a name www.example.com
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

go deeper

for a junior

Know that DNS on a Mac is changed through network settings or the networksetup command, not by editing /etc/resolv.conf, and that scutil --dns shows what is in use.

for a middle

Explain the mechanism: configd holds network configuration in the dynamic store, mDNSResponder resolves from it, and /etc/resolv.conf is generated output kept for legacy BIND-linked callers.

for a senior

Show the diagnosis: compare dig against dscacheutil to separate a server problem from a client one, read scutil --dns for scoped split-DNS resolvers a VPN installed, and flush the cache before escalating.

for a principal

Own the fleet position: DNS for managed Macs belongs in a configuration profile rather than per-machine networksetup calls, and split-DNS from VPN clients needs a documented precedence story so internal name resolution is predictable.

## The Linux habit that fails here On a Linux host you can reason about resolution by reading `/etc/nsswitch.conf` and `/etc/resolv.conf`. Bringing that habit to macOS produces this bug report every time. macOS does have a `/etc/resolv.conf`, and it does contain `nameserver` lines, and it is nevertheless not where the system's DNS configuration lives. ## Where it does live `configd` maintains the **dynamic store**: a live, in-memory database of network state assembled from DHCP leases, manually configured network services, VPN connections and configuration profiles. DNS settings are one branch of it, and they are *per network service* — Wi-Fi can have one set of resolvers while an active VPN supplies another, plus resolvers scoped to particular domains. `mDNSResponder` — the unified responder that handles both unicast DNS and multicast/Bonjour names — consumes that configuration and answers on behalf of applications, which reach it through the system resolver APIs. It also owns the DNS cache. `/etc/resolv.conf` is a symlink to a file `configd` writes out as a courtesy for software that links the classic BIND resolver directly instead of using the system APIs. Because it is generated, editing it is pointless twice over: the programs you care about never read it, and the next network change regenerates it. ## Changing DNS properly `networksetup` is the supported command-line front end to network service configuration: ```bash networksetup -listallnetworkservices networksetup -getdnsservers Wi-Fi sudo networksetup -setdnsservers Wi-Fi 1.1.1.1 8.8.8.8 sudo networksetup -setdnsservers Wi-Fi Empty # revert to DHCP-supplied networksetup -getsearchdomains Wi-Fi ``` Note the argument is a **network service name** ("Wi-Fi", "Ethernet", "USB 10/100/1000 LAN"), not a BSD interface name like `en0`; listing the services first is how you get the exact string, which is case- and spelling-sensitive. The literal `Empty` clears the manual override rather than setting a server named Empty. On a managed Mac, DNS may be set by a configuration profile instead, in which case it is enforced centrally and a local `networksetup` change is the wrong lever. ## Inspecting what is really in effect `scutil --dns` is the answer to "what will this machine actually do with a lookup": ``` resolver #1 search domain[0] : corp.example.com nameserver[0] : 10.0.0.53 if_index : 14 (en0) flags : Request A records reach : 0x00020002 (Reachable,Directly Reachable Address) resolver #2 domain : internal.example.com nameserver[0] : 10.9.9.9 ``` The ordering and the scoping are the point. Resolver #1 is the default; later entries with a `domain` line are **split DNS** — queries for that suffix go to that server only. This is how a VPN routes internal names to internal resolvers while everything else stays on the local network, and it is invisible from `/etc/resolv.conf`. `scutil` more generally is the client for the dynamic store; interactively, `scutil` then `show State:/Network/Global/DNS` prints the global DNS dictionary. ## Why dig and the browser disagree This is the classic follow-on symptom. `dig` and `nslookup` are BIND tools: they build queries themselves and read `/etc/resolv.conf` for a server, bypassing `mDNSResponder` entirely. So `dig` ignores the DNS cache, ignores split-DNS scoping, and ignores `/etc/hosts`. A name can resolve in `dig` and fail in Safari, or vice versa, purely because the two took different paths. To query through the same path an application uses, use `dscacheutil -q host -a name www.example.com`, which goes through Directory Service and the system resolver. Comparing the two answers is a fast way to tell a *server* problem from a *client configuration or cache* problem. ## Cache and hosts file `mDNSResponder` caches positive and negative answers. The standard flush is: ```bash sudo dscacheutil -flushcache sudo killall -HUP mDNSResponder ``` Both halves are conventional: the first clears the Directory Service cache, the second signals the responder to reload. `/etc/hosts` *is* honoured — it is consulted through the system resolver path, so a hosts entry affects applications but will not affect `dig`. ## What the interviewer is listening for That you know configuration is centralised in the dynamic store, that `/etc/resolv.conf` is generated output, that `networksetup` sets and `scutil --dns` inspects, and — the mark of someone who has actually debugged this — that `dig` takes a different path from the applications, which is why the two can disagree.

  • A hostname resolves correctly with dig but the application still cannot reach it. What does that tell you?
    That the authoritative answer is fine and the problem is on the client side of the system resolver. `dig` builds its own query and reads `/etc/resolv.conf`, so it skips `mDNSResponder`'s cache, the split-DNS scoping in the dynamic store, and `/etc/hosts`. Re-query through the application path with `dscacheutil -q host -a name <host>`, check `scutil --dns` for a scoped resolver capturing that suffix, and flush the cache before blaming the server.
  • How does a VPN change DNS resolution on a Mac without touching /etc/resolv.conf?
    It publishes resolver configuration into the SystemConfiguration dynamic store for its own interface, usually with one or more domains scoped to it. `mDNSResponder` then sends queries for those suffixes to the VPN's resolvers and everything else to the default resolver. `scutil --dns` shows this as additional resolver entries carrying a `domain` line and the VPN's interface index.
  • Why does networksetup take "Wi-Fi" rather than en0?
    `networksetup` operates on network *services* — the named, ordered configurations you see in network settings — not on BSD interfaces. A service maps to an interface but carries its own IP, DNS and proxy configuration, and several services can exist for one piece of hardware. Get the exact strings from `networksetup -listallnetworkservices`; they are spelling-sensitive.

saying these in an interview costs you the question

  • Editing /etc/resolv.conf and expecting applications to follow it
  • Assuming dig proves what the applications will resolve
  • Passing en0 to networksetup instead of the service name
  • Believing macOS has a single global resolver rather than scoped ones
  • Thinking /etc/hosts is ignored on macOS

context