Build1 publisher3 min readPublished
Querying the zone's own nameservers removes one of seven DNS false alarms
The author of DNS Notify lists seven kinds of change alert that mean nothing, and only the first is cured by changing where the query goes. The other six get fixed after the answer arrives.
The Engineer · Build desk

What happened
- The write-up behind DNS Notify starts from the ten-line checker everyone writes, which mails on any difference from the stored value and promptly alerts on records nobody has touched.
- Queried through a public anycast resolver, a record mid-change comes back old, new, old, new until the TTL expires everywhere, and every flip looks like an edit to a checker that diffs strings.
- The published fix resolves the zone's NS records, resolves each nameserver's address, then queries those addresses from a dnspython Resolver built with configure=False and a 10-second lifetime.
- Comparison runs on a canonical form, with trailing dots stripped, MX and SRV answers rebuilt field by field and TXT strings joined, because a long SPF or DKIM record arrives in 255-byte pieces.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint A checker that stores the raw answer line inherits the operator's own TTL edits, so the pre-migration TTL drop pages the on-call before the migration has started.
- cost Network blips stay quiet, and a record deleted for real is reported four check cycles late; whoever set the interval decides how bad that is.
- decision Anyone writing their own checker has to pick the canonical form before storing the first baseline, because every later comparison runs against that stored text.
The dnspython function in the post resolves in three steps, and only the third one leaves the host resolver behind. `dns.resolver.resolve(domain, "NS")` runs through the module default, and so do the A lookups for each nameserver host; the explicit IP list is assigned afterwards, to a `Resolver(configure=False)` with `timeout, lifetime = 5, 10` [8][9]. The checker asks a cache which servers are authoritative, then asks those servers what the record says. That split is fine for MX and TXT drift, but if the delegation itself moves, the answer about who to trust came from the cache.
Sort the seven false alarms by where the fix lives and six of them land in the same place. Cache flap is the one cured by changing the query target [6][7]. Rotation, TTL, the SOA serial, timeout-versus-deletion, wire formatting and flap-back are all handled after the answer arrives, in the comparison or in stored state [19].
TTL is the case where the better query only half helps. An authoritative server returns a stable TTL, but operators lower it before a migration and raise it after, so the field is kept for display and left out of the comparison [11].
The SOA serial goes up on every zone edit, and some providers bump it on their own schedule, which means diffing the whole record duplicates every alert you already send [12]. Masking the serial keeps the rest of the record live, so a change to the primary nameserver or to the timers still comes through [12].
Failures are a separate code path from answers. NXDOMAIN and an empty answer are real answers; a timeout or SERVFAIL is a failed check, counted per record, with the alert held until four consecutive runs fail, and the recovery logged without a second email [15]. The author of DNS Notify wrote that conflating the two turns every network blip into "your MX record was removed", "which is a horrible email to get at 3am when it is not true" [13][14]. That threshold has a price: a record deleted for real waits four check cycles before anyone hears about it, and the post does not give the interval between checks [20].
The six comparison fixes only matter in your checker if your zone looks like the author's. Multi-value answers get rotated by many nameservers on purpose, so `.10, .20` becomes `.20, .10` between runs [10]. Long TXT records arrive as several quoted 255-byte strings with server-chosen split points, so SPF and DKIM are the records canonicalisation is there for [16]. A single-provider zone that never rotates and whose TTLs never move will not trigger most of the six. Cache flap still fires, because it is a property of the resolver you queried [5][6].
The part I would copy first is the two-value state. Each record keeps an accepted value and a current value; when current returns to accepted, the pending flag clears and a "reverted" event is logged with no email [17]. Accepting a new value after a planned migration rewrites the baseline, and without that step the monitor nags forever about a change you made on purpose [18].
What to watch
- Whether the checker ever validates the NS set against the parent delegation instead of a cached NS lookup.
- Whether DNS Notify publishes its check interval, which would turn the four-failure threshold into a stated detection delay.
- Whether the canonical form is documented per record type, so a self-hosted checker can store baselines that survive a rewrite of the comparison code.