Concepts · DNS

DNS monitors

Watch the records your mail and routing depend on — SPF, DMARC, DKIM, MX, CNAME — and the blocklists your sending reputation lives on. Catch a silent DNS change or a fresh listing before deliverability breaks.

Nightlamp has two DNS check types. A dns_record monitor resolves a hostname's records on each probe and asserts the answer — either an exact set (a drift detector) or that it contains a substring (for SPF/DMARC content). A dnsbl monitor looks an IP or hostname up against DNS blocklist zones and fails when any zone lists it. Both run on the normal probe schedule and reuse request_timeout_seconds.

What is a DNS record monitor?

DNS is configuration that lives outside your app, so an outage there is invisible to an HTTP check — the site still returns 200 while your mail silently fails SPF or your MX points at a decommissioned host. A dns_record monitor resolves the record from a public resolver every interval and compares the answer two independent ways:

  • dns_hostname — the fully-qualified name to resolve, e.g. _dmarc.example.com.
  • dns_record_type — one of A, AAAA, MX, TXT, CNAME, NS, SOA.
  • dns_expected_values — optional exact set (max 20). The resolved answer must match this list exactly; any add, drop, or change fails as drift.
  • dns_value_contains — optional substring. The resolved answer must contain this text. Ideal for SPF/DMARC, where the whole record changes but one clause must stay.
  • dns_last_resolved_values — the answer set from the most recent probe; read-only.

Set either assertion, both, or neither — with neither, the monitor just checks that the name resolves at all. Two conventions trip people up: MX values come back with the priority and a trailing dot, like 10 mx1.example.com.; and TXT content is matched without the surrounding quotes the zone file shows.


Exact set vs contains — which do I use?

Use dns_expected_values (exact set) when the whole record should be stable and you want to know the moment anything moves — MX hosts, a CNAME target, a pinned DKIM key. It's strict on purpose: a reordered or extra value is drift.

Use dns_value_contains when the record legitimately carries more than you care about and you only need one clause to survive — the classic case is SPF and DMARC, where you assert include:_spf.google.com or p=reject stays present even as the rest of the record evolves.

Create — DMARC policy watch

curl -X POST https://api.nightlamp.app/apps/$APP_ID/monitors \ -H "Authorization: Bearer $NIGHTLAMP_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name":"DMARC policy","check_type":"dns_record", "dns_hostname":"_dmarc.example.com","dns_record_type":"TXT", "dns_value_contains":"p=reject"}'

SPF, DMARC, DKIM, and MX examples

Email authentication is the highest-value thing to watch here — a dropped SPF include or a DMARC policy quietly downgraded to p=none doesn't break your site, it breaks your deliverability days later. Match TXT content without quotes.

SPF — substring

# SPF — assert the include clause is still present (substring match). # The TXT content is matched WITHOUT the surrounding quotes. { "dns_hostname": "example.com", "dns_record_type": "TXT", "dns_value_contains": "include:_spf.google.com" }

DMARC — substring

# DMARC — assert the policy stays at reject. { "dns_hostname": "_dmarc.example.com", "dns_record_type": "TXT", "dns_value_contains": "p=reject" }

For DKIM, pin the selector record as an exact set so a rotated or truncated key surfaces immediately:

DKIM — exact set

# DKIM — pin the whole selector record as an exact set so a rotated or # truncated key fails as drift. TXT content, no surrounding quotes. { "dns_hostname": "selector1._domainkey.example.com", "dns_record_type": "TXT", "dns_expected_values": ["v=DKIM1; k=rsa; p=MIGfMA0GCSq..."] }

For MX, the exact set is your routing drift detector — remember the priority prefix and the trailing dot on each value:

MX — exact set

# MX — exact-set drift detector. Values carry the priority and a # TRAILING DOT, exactly as the resolver returns them. { "dns_hostname": "example.com", "dns_record_type": "MX", "dns_expected_values": ["10 mx1.example.com.", "20 mx2.example.com."] }

What is a DNSBL monitor?

A dnsbl monitor checks whether an IP address or hostname is listed on one or more DNS blocklists. If you send email, a fresh listing is a deliverability emergency you want to hear about before your bounce rate spikes — not after.

  • dnsbl_target — the IPv4, IPv6, or hostname to check, e.g. 203.0.113.10.
  • dnsbl_zones — optional list of blocklist zones to query (max 10). Defaults to PSBL + SpamCop.
  • dnsbl_last_listed_zones — the zones that listed the target on the last probe; read-only. Empty means clean.

Create — sending IP reputation

curl -X POST https://api.nightlamp.app/apps/$APP_ID/monitors \ -H "Authorization: Bearer $NIGHTLAMP_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name":"Sending IP reputation","check_type":"dnsbl", "dnsbl_target":"203.0.113.10", "dnsbl_zones":["psbl.surriel.com","bl.spamcop.net"]}'

Which blocklists actually work from here?

This matters more than it looks. Many well-known blocklists only answer queries from resolver IPs they recognize — and a blocklist that won't answer your query returns the same "not found" response as a target that's genuinely clean. A monitor pointed at a zone that won't talk to our probes reports a permanent, silent all-clear.

Nightlamp's defaults are chosen to avoid exactly that trap:

  • PSBL (psbl.surriel.com) and SpamCop (bl.spamcop.net) answer from any resolver without registration, so they work from our probes out of the box. These are the built-in defaults.
  • Spamhaus is opt-in. Add <your-DQS-key>.zen.dq.spamhaus.net to your zones — it needs a free Spamhaus DQS (Data Query Service) key, and the key is embedded in the query zone. Spamhaus's public zones block queries from cloud / Lambda IP ranges and answer NXDOMAIN, which would silently report "clean" — so do not use the public zone here.
  • Barracuda (b.barracudacentral.org) is opt-in and requires registering your resolver IP with Barracuda first. Until it's registered it stays silent and reads as clean.

Add Spamhaus (DQS key)

# Spamhaus is opt-in — it needs a free DQS (Data Query Service) key, and # its query zone embeds that key. The PUBLIC zen.spamhaus.org zone blocks # queries from cloud / Lambda IP space and answers NXDOMAIN, which reads as # a false "clean". Use the DQS zone from a registered key instead. { "dnsbl_target": "203.0.113.10", "dnsbl_zones": [ "psbl.surriel.com", "bl.spamcop.net", "<your-DQS-key>.zen.dq.spamhaus.net" ] }

Add Barracuda (registered)

# Barracuda is opt-in — b.barracudacentral.org only answers resolvers whose # IP you have registered with them. Unregistered, it stays silent and reads # as "clean". Register first, then add the zone. { "dnsbl_target": "203.0.113.10", "dnsbl_zones": ["psbl.surriel.com", "bl.spamcop.net", "b.barracudacentral.org"] }

When does Nightlamp open an incident?

For dns_record, the probe fails — and an incident opens through the normal alert pipeline — when the exact set doesn't match (drift) or the required substring is absent, as well as on a resolution error or timeout. For dnsbl, the probe fails when any configured zone lists the target. When the next probe resolves cleanly or the listing clears, the incident resolves automatically. Create the monitor like any other — pick DNS record or DNS blocklist (DNSBL) as the type — and its check-type is fixed after creation, while the assertions, zones, and interval stay editable.


Managing the DNS you're watching?

Monitoring catches record drift after it happens; the cleaner fix is DNS that's easy to move and hard to break in the first place. If you host one of these zones on Route 53 and want out, DNSCove's Route 53 migration guide does a zero-downtime cutover on a wire-compatible authoritative DNS provider, and its cert-manager DNS-01 guide issues Let's Encrypt certificates through the same TXT records this check watches — so a renewal never becomes the outage you're monitoring for.