ObservabilityXI · Blackbox MonitoringBlackbox
DNS Probes
What you'll learn
- Configure the dns module with the right query name, query type, and protocol
- Validate resolved answers with regex matches and authority checks
- Distinguish a recursion-required probe from a cache-only probe and choose the right one
- Decide when a DNS probe is more honest than a TCP probe for an upstream service
Prerequisites
Verified against Prometheus 2.55.x · Alertmanager 0.28.x · node_exporter 1.8.x · blackbox_exporter 0.26.x · Grafana 11.x · Loki 3.x · Tempo current · OpenTelemetry Collector 0.110.x · Grafana Alloy current · Docker Engine 28.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-13
A Slack channel lights up. Users cannot reach the customer
portal. The on-call engineer opens the blackbox dashboard and
the row for the portal is green. Every internal panel is green.
The engineer is about to declare “no incident” when a colleague
runs dig portal.example.com from a residential connection and
gets a SERVFAIL. The internal resolver cached the answer yesterday;
the public resolver has been returning SERVFAIL for four hours.
The blackbox dashboard never asked the same question the user
asked. This lesson is about the probe that does.
What it is
The dns module of blackbox_exporter issues a DNS query for a
configured name and type, optionally validates the response
against a regex, and returns success or failure as a normal
probe_success metric. The metric shape is the same as every
other module; the protocol-specific metrics are
probe_dns_lookup_time_seconds (the time inside the resolver
call) and the labels added by relabeling.
The DNS probe is the only module in the default catalogue whose question is what name resolves to what address for the resolver the probe is using. The TCP probe answers whether the port is open; the DNS probe answers whether the lookup returns what is expected.
Why a sysadmin cares
Two operational facts make the DNS probe a load-bearing component of the platform:
- The user depends on the resolver. A misconfigured delegation, an expired DNSSEC signature, a parent zone that has not been updated since the CNAME was added. The user resolves and fails before any other layer has a chance to work. The application is up; the user cannot reach it.
- The application depends on its own resolver configuration. Many applications cache the answer at startup and never refresh it. A stale CNAME persists for the lifetime of the process. The DNS probe catches the upstream change before the user does; the cache TTL does not bind the probe.
The DNS probe is also the cheapest check you can run against the public DNS. A DNS probe across one hundred records at a five-second scrape interval is still cheaper than a single TCP probe at thirty seconds against the same one hundred targets, because the resolver does the heavy lifting.
How it works
The exporter invokes the Go net.Resolver, builds a DNS
message, and sends it over UDP (default) or TCP to the
configured resolver. The resolver returns an answer; the
exporter parses the answer and applies the validation rules in
order.
exporter host resolver authoritative NS
| | |
| --- A shop.example.com? --> | |
| | --- zone lookup -----> |
| | |
| | <--- 10.20.4.1 ------- |
| | ttl=300 |
| <-- 10.20.4.1 ------------ | |
| ttl=300 | |
| | |
v v v
validate_answer_rrs probe_dns_lookup_time_seconds =
regex match against seconds
returned IP
probe_success=1
Three configuration choices govern the rest:
protocol: udporprotocol: tcp. UDP is the default;tcpis required when the answer set or the operator policy forces a TCP fallback. A probe that selectstcphonours the network operator’s choice; a probe that selectsudpmay receive a truncated answer for large records.query_name. The name to resolve. The probe is honest only if the name matches what the application and the user resolve.query_type.A,AAAA,CNAME,MX,TXT,SRV, and a small set of others. The right type is the one the application would query for.preferred_ip_protocolandip_protocol_fallback. Same semantics as the TCP and ICMP modules.
The validation rules are applied to the answer section, the authority section, and the additional section independently. Each rule says: fail if a record matches a configured regex; fail if a record does not match a configured regex. The combination is the validation the team needs.
validate_answer_rrs:
fail_if_matches_regexp: # any record matching ANY pattern fails
- "127\\."
fail_if_not_matches_regexp: # at least one record must match
- "^(10\\.20\\.[0-9]+\\.[0-9]+)$"
validate_authority_rrs:
fail_if_matches_regexp:
- "0\\.0\\.0\\.0"
A common production shape:
fail_if_matches_regexpcatches bogus answers:127.0.0.1,0.0.0.0, addresses in a deny-listed CIDR.fail_if_not_matches_regexpcatches wrong answers: a DNS answer forshop.example.comthat resolves to an address in a network the team did not deploy.
How to configure it
The DNS module is more expressive than the others, mostly because of the validation step. A useful production module covers the question, the type, the protocol, and the validation.
# /etc/blackbox/blackbox.yml
modules:
# Public resolver probing for the customer portal.
# udp, A record, IPv4 first, fall back to IPv6.
dns_portal_a:
prober: dns
timeout: 3s
dns:
preferred_ip_protocol: ip4
ip_protocol_fallback: true
protocol: udp
query_name: portal.example.com
query_type: A
validate_answer_rrs:
fail_if_matches_regexp:
- "127\\."
- "0\\.0\\.0\\.0"
fail_if_not_matches_regexp:
- "^(10\\.20\\.[0-9]+\\.[0-9]+|192\\.0\\.2\\.[0-9]+)$"
# Authoritative server check, TCP, IPv6 only.
dns_ns_aaaa:
prober: dns
timeout: 5s
dns:
preferred_ip_protocol: ip6
ip_protocol_fallback: false
protocol: tcp
query_name: ns1.example.com
query_type: AAAA
validate_answer_rrs:
fail_if_not_matches_regexp:
- "^(2a00:[a-f0-9:]+)$"
# Recursion-required probe against a recursive resolver.
# The resolver must walk the delegations to answer.
dns_recursion_google:
prober: dns
timeout: 3s
dns:
resolver: 8.8.8.8:53
query_name: shop.example.com
query_type: A
protocol: udp
recursion_desired: true
validate_answer_rrs:
fail_if_matches_regexp:
- "127\\."
# Cache-only probe against the local resolver.
# After the upstream has answered once, the cache hits
# without recursion. Catches resolver outages more sharply.
dns_cache_internal:
prober: dns
timeout: 2s
dns:
resolver: 10.20.0.53:53
query_name: shop.example.com
query_type: A
protocol: udp
recursion_desired: false
The scrape job:
# /etc/prometheus/prometheus.yml
scrape_configs:
- job_name: blackbox_dns_portal
metrics_path: /probe
params:
module: [dns_portal_a]
scrape_interval: 30s
static_configs:
- targets: ['portal.example.com']
labels:
service: portal
env: prod
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- target_label: module
replacement: dns_portal_a
EDNS0 and large answers
EDNS0 is the extension mechanism that lets DNS answers exceed
the original 512-byte UDP limit. The dns module operates
with EDNS0 by default in modern versions; an operator who
deliberately disables it must understand that records above
512 bytes will receive a truncated response over UDP and the
exporter will fail. In production, never disable EDNS0 to
match a broken upstream — use protocol: tcp instead.
How to validate it
# 1. Resolve the same way the probe does.
dig +short portal.example.com A
# 10.20.4.7
# 10.20.5.7
# 2. Run the probe by hand against the blackbox exporter.
curl -sf "http://blackbox:9115/probe?module=dns_portal_a&target=portal.example.com" \
| grep -E '^probe_'
# probe_dns_lookup_time_seconds 0.018
# probe_duration_seconds 0.020
# probe_success 1
# 3. Test against a name whose answer set should fail the regex.
curl -sf "http://blackbox:9115/probe?module=dns_portal_a&target=loopback.example.com" \
| grep -E '^probe_'
# probe_failed_due_to_regex 1
# probe_success 0
# 4. Recursion-required probe against a name we expect SERVFAIL.
dig +dnssec shop.example.com @8.8.8.8
# ;; Got SERVFAIL
curl -sf "http://blackbox:9115/probe?module=dns_recursion_google&target=shop.example.com" \
| grep -E '^probe_'
# probe_success 0
# probe_dns_lookup_time_seconds 1.203
# 5. The local cache view.
dig +norecurse shop.example.com @10.20.0.53
# ;; Got SERVFAIL (only authoritative answers are returned by recursions-disabled servers when the local cache does not have it)
curl -sf "http://blackbox:9115/probe?module=dns_cache_internal&target=shop.example.com" \
| grep -E '^probe_'
# probe_success 0
The exporter log records the underlying Rcode when the resolver returns one: NOERROR, NXDOMAIN, SERVFAIL, REFUSED.
How it can fail
-
Validator regex too aggressive. The
fail_if_not_matches_regexppattern rejects a legitimate address because the operator pinned a CIDR and the cloud provider moved the service to a new range. Symptom: probe returnsprobe_failed_due_to_regex=1, the user is healthy. Fix: validate the shape of the answer, not the content, when the content is volatile. -
Validator regex too loose. The
fail_if_matches_regexplist omits a deny-listed CIDR. The resolver returns an address the operator did not intend. Symptom: probe green; the user reaches a blackhole. -
Resolver pool exhausted. A probe set targets the local resolver. A burst of 100 DNS probes in five seconds with
recursion_desired: trueexhausting the resolver’s outbound socket pool. Symptom: probes timeout during the burst; the resolver CPU is at the ceiling. -
Wrong query type. The application needs
CNAME, the probe asks forA. The CNAME points to a CDN that rotates A records; the A record the probe sees is from a regional cluster the user does not reach. Symptom: probe green; the user reaches the wrong cluster. -
Wrong protocol for the operator policy. UDP is filtered at the perimeter; the operator policy mandates TCP. UDP probes return SERVFAIL or timeout. Symptom: every probe
0; a TCP probe in the same module succeeds. -
EDNS0 disabled by the operator, large answer is truncated. The probe receives a truncated response and marks the probe as failed. Symptom:
probe_success=0for a name whose answer exceeds 512 bytes; switchprotocol: tcpto confirm. -
Cache poisoning probe. A probe that resolves through a third-party resolver can be diverted to a server the operator did not intend. Symptom: probe green; the address the resolver returned is in a deny-listed CIDR; the answer was valid against the regex but not against the operator’s threat model.
-
Authoritative DNS outage masquerading as a probe failure. The probe expects the answer; the resolver returns SERVFAIL because the parent zone was unreachable. Symptom: every
dns_*probe goes red; the resolver log names the upstream failure.
How to troubleshoot it
Order matters; the cost of chasing the wrong layer is high.
- Run
digfrom the exporter host against the same resolver as the probe. This isolates the failure to either the network path or the resolver’s view. - Run the probe by hand.
curl "http://exporter:9115/probe?module=...". Readprobe_failed_due_to_regex,probe_dns_lookup_time_seconds, and any log lines the exporter emitted during the call. - Test against a known-good name. A module that probes
dns_portal_aagainstdns_portal_areturningprobe_success=0fordns_portal_aagainstdns_google_publicreturningprobe_success=1proves the path is fine and the failure is in the resolver answer set. - Compare cache-only and recursion-enabled probes. The difference between them names the upstream resolver as the failure point or the local cache as the failure point.
- Cross-check with the application resolver. The
application uses
dnsmasq/Unbound/systemd-resolved; the probe uses the system resolver. Read the application config to find out which resolver the user actually uses. - Look for SERVFAIL. SERVFAIL is its own signal. A SERVFAIL answer is not a TCP timeout, not a regex miss, not an NXDOMAIN. The exporter’s log records the Rcode; the operator’s response depends on it.
- Validate the regex. Run the probe against a name whose answer is known; the regex should pass. Run the probe against a known-bad name; the regex should fail.
Security implications
The DNS probe enforces a validation, not all validations.
A regex that matches the shape of an answer does not validate
that the answer is non-malicious. Operators should consider
DNSSEC validation through the resolver, since the exporter
itself does not perform DNSSEC checks; the resolver’s
trust-anchors configuration governs that.
The probe is also an information disclosure. A module that resolves internal names against the local resolver and publishes the answers as metric series has just leaked the internal address space into Prometheus. Tighten the labels and the access controls.
Cache poisoning is a real risk. A probe that runs against a public resolver without DNSSEC validation can be redirected to an attacker-controlled IP, and the regex may pass because the shape is correct. The threat model is the operator’s choice; the lesson recommends recursion_desired only against resolvers the team controls, or against a trusted path with DNSSEC validation in place.
Performance implications
The DNS probe is the cheapest of the protocol probes; the resolver is the work unit, and the exporter just frames the question. Two pressure points exist:
- Resolver upstream rate. A recursion_desired probe against a name whose upstream is slow multiplies the latency. A scrape interval too low can cause the resolver to queue, which causes the probe to time out, which produces a false-positive outage.
- Answer validation cost. A
fail_if_matches_regexpapplied to a large answer set is a regex evaluation per record. The cost is bounded but not zero.
A production scrape interval of thirty seconds with two or three modules per service is reasonable. Tighter intervals or more modules are possible but the trade-off is resolver load, not exporter load.
Production guidance
- Use the recursion-desired probe against the resolver the user uses. The cache-only probe is a different question.
- Validate the shape and the deny-list; do not pin the exact CIDR when the answer is volatile.
- For records with answers larger than 512 bytes, prefer
protocol: tcp. Do not disable EDNS0 to make a probe pass. - Run at least one probe from each user-facing region. The regional view of DNS is not the same.
- When SERVFAIL appears, look at the resolver upstream, not the probe.
- Treat
probe_dns_lookup_time_secondsas a metric of interest in its own right. The drift to 200 ms precedes the SERVFAIL.
Verification
You should now be able to answer:
- Why is a DNS probe more honest than a TCP probe for an upstream whose name is part of the user path?
- What is the difference between a recursion-required probe and a cache-only probe, and when does each answer the right question?
- Why does the
fail_if_matches_regexprule exist alongsidefail_if_not_matches_regexp? What does each catch? - Why might a SERVFAIL response deserve a different response than a regex miss?
- When should
protocol: tcpbe preferred over the defaultudp?
Quiz
Knowledge check · 8 questions
Q1. What does the dns module of blackbox_exporter actually verify?
Q2. A user reports the site is unreachable. The DNS probe against the same name returns green. The most likely explanation is:
Q3. Which validation rules does the dns module accept? Select all that apply.
Q4. A DNS probe with recursion_desired: true caches the answer in the local resolver after the first call.
Q5. Name the gauge metric that records the time the resolver took to return an answer.
Q6. EDNS0 is disabled at the resolver and the answer is 600 bytes. The DNS probe over UDP with the default protocol returns 0. The right next step is:
Q7. A regulator-required regex pins a CNAME to a specific target. The CDN rotates the target. The probe now fails. The right response is:
Q8. Why does a cache-only probe (recursion_desired: false) catch resolver outages more sharply than a recursion-required probe?
Passing score: 75%. Answers are checked in this browser.