skip to content

A service resolves hostnames correctly on a Debian host but misbehaves on Alpine Linux: an entry added to `/etc/nsswitch.conf` has no effect, and a lookup returning a large answer fails. How does musl's resolver differ from glibc's, and what does that mean operationally?

level: seniorimportance: should knowfreq 40%

answer

  1. musl brought its own resolver
  2. no plugin mechanism for name sources
  3. nsswitch.conf is read by nobody
  4. nameservers queried in parallel, first wins
  5. TCP fallback arrived only in 1.2.4

basics

~20 s

musl implements its own built-in stub resolver instead of glibc's Name Service Switch. It ignores /etc/nsswitch.conf entirely, so NSS plugins never load, and it queries every nameserver in /etc/resolv.conf in parallel rather than in order.

solid answer

~40 s

Alpine uses musl, and musl deliberately has no Name Service Switch. glibc's `getaddrinfo` consults `/etc/nsswitch.conf` and can dlopen plugin modules for LDAP, mDNS, SSSD or a caching daemon; musl hard-codes the lookup as `/etc/hosts` first, then DNS from `/etc/resolv.conf`, and never reads `nsswitch.conf` at all — so directory integration and plugin-based name sources simply do not work, silently. musl also queries all configured nameservers in parallel and takes the first usable answer, whereas glibc tries them in order with a timeout; if a secondary resolver serves a different view, musl can race to the 'wrong' one. It sends the A and AAAA queries concurrently, which some middleboxes handle poorly. And musl only gained TCP fallback for truncated responses in 1.2.4, so older Alpine releases fail on large answers rather than retrying over TCP.

code

bash · 3 lines
bash
apk info -v musl            # which musl version this Alpine ships
/lib/ld-musl-x86_64.so.1    # run the loader bare: prints the musl version banner
cat /etc/resolv.conf        # the only name configuration musl actually reads

go deeper

for a junior

Know that name resolution is done by the C library, so Alpine's musl can behave differently from glibc even with identical /etc/resolv.conf contents on the same network.

for a middle

Explain that musl has no Name Service Switch, so /etc/nsswitch.conf is ignored and plugin-based sources never load, and that musl queries all configured nameservers in parallel instead of in order.

for a senior

Show you can diagnose it: reproduce through getaddrinfo rather than a DNS tool, check the musl version before assuming TCP fallback exists, and recognise a parallel-query race against divergent resolvers as the cause of intermittent wrong answers.

for a principal

Own the base-system consequence. Rule on whether workloads needing directory integration or plugin-based name sources may run on a musl distribution at all, since the gap cannot be configured away and surfaces as production incidents rather than build failures.

## Two different designs, not a bug Name resolution on Linux is not a kernel service. `getaddrinfo` is a libc function, so what happens when a program resolves a hostname depends entirely on which libc it is linked against. glibc and musl made opposite architectural choices here, and every Alpine DNS surprise follows from that. ## glibc: pluggable by design glibc routes host, user and group lookups through the **Name Service Switch**. `/etc/nsswitch.conf` lists, per database, the sources to consult in order — `hosts: files dns` being the common case — and each named source corresponds to a shared object that glibc loads at run time. That is how a host gets users from LDAP or SSSD, hostnames from mDNS, or a local caching daemon in the path, without any application knowing. ## musl: one resolver, no plugins musl rejects that model. Loading arbitrary shared objects into every process that resolves a name conflicts with musl's goals around static linking, size and predictability. So musl has no NSS at all: `/etc/nsswitch.conf` is not parsed, not warned about, just ignored. Host lookup is fixed as `/etc/hosts` first, then DNS using the nameservers in `/etc/resolv.conf`. The operational consequence is that an entire category of configuration becomes a no-op. Directory-backed users and groups, mDNS names, and plugin-based caching do not work on Alpine, and nothing tells you — the file is present, your edit is in it, and it means nothing. On a system that was expected to join a corporate directory, that is a design constraint, not a tunable. ## Parallel queries instead of ordered fallback Given several `nameserver` lines, glibc contacts the first and moves to the next on timeout or failure, so an ordering in `resolv.conf` is a genuine preference. musl sends the query to all listed nameservers at once and uses the first usable reply. It is faster and more resilient when a resolver is down — no waiting out a timeout — but it discards the notion of a primary. If your second nameserver serves a split-horizon or stale view, musl will sometimes return that view purely because it answered first, producing intermittent, host-dependent wrong answers that look like caching but are not. musl also issues the A and AAAA queries concurrently. Correct resolvers cope; some older middleboxes and load balancers handle two outstanding queries poorly and drop one, which shows up as sporadic resolution failures on Alpine only. ## Truncation and TCP fallback A UDP DNS response that does not fit sets the TC (truncated) bit, and the client is expected to retry over TCP. musl did not implement that fallback until version 1.2.4, released in 2023 and shipped by Alpine releases from that year onward. On an older Alpine, a name whose answer set is large — a service with many addresses, or a record carrying a lot of data — fails to resolve while the identical query succeeds from a glibc host on the same network. Check what you are actually running before theorising: ``` apk info -v musl /lib/ld-musl-x86_64.so.1 # prints the musl version banner ``` ## What is not different Two things worth stating so you do not chase them. First, neither libc caches DNS answers on its own — a plain glibc host without a caching daemon re-queries just like musl does, so 'Alpine has no DNS cache' is not a distinction. Second, `/etc/resolv.conf` itself is read by both, and both honour `nameserver` and search-domain configuration; the divergence is in the strategy applied to that configuration, not in the file's location or basic syntax. ## How to diagnose Work out which layer is answering. Comparing a lookup from a resolver tool against what the application sees is misleading here, because a standalone DNS tool talks to the network directly while the application goes through libc — that gap is exactly where an NSS-versus-musl difference hides. Reproduce with a small program that calls `getaddrinfo` on the affected host, capture traffic to see whether queries go out in parallel and whether a truncated reply came back, and check the musl version before assuming a behaviour is present. If the requirement is genuinely directory integration or a plugin-based name source, the honest conclusion is that the workload does not belong on a musl system. ## Why this is a senior question It is only answerable if you know that name resolution lives in libc rather than the kernel, that a distribution's libc choice therefore changes application behaviour, and that a configuration file can be present and completely inert. Those are the instincts that let someone debug a machine they did not build.

  • A DNS lookup tool on the Alpine host returns the right answer but the application does not. What does that tell you?
    That the two are not doing the same thing. A standalone DNS client builds and sends its own query straight to a resolver, while the application calls `getaddrinfo` in musl, which checks /etc/hosts first and then applies musl's own query strategy. A stale /etc/hosts entry, a search-domain difference, or a parallel-query race all live in that gap. Reproduce through `getaddrinfo`, not through the tool.
  • Why can musl's parallel querying produce intermittently different answers?
    Because it removes the notion of a primary nameserver. glibc tries the resolvers in order, so the first one's view wins unless it fails; musl asks all of them at once and takes whichever replies first, which varies with load and latency. If the resolvers do not serve identical data — split-horizon views, one lagging a zone update — the same host resolves differently on successive attempts for no reason visible in the config.
  • What breaks on Alpine if a workload needs users and groups from a corporate directory?
    The integration itself. Directory-backed user and group lookup on glibc is delivered through NSS modules, and musl has no NSS, so there is no supported plugin path — editing /etc/nsswitch.conf changes nothing. Either the workload moves to a glibc-based distribution, or the identity data has to reach it some other way entirely. This is a reason to choose a base system deliberately rather than by image size.

saying these in an interview costs you the question

  • Says musl reads /etc/nsswitch.conf like glibc does
  • Claims DNS resolution is a kernel feature, not libc
  • Thinks glibc caches DNS answers by default and musl does not
  • Assumes the first nameserver line is preferred on musl
  • Believes adding an NSS module fixes lookups on Alpine

context