coredns/coredns
How to monitor
CoreDNS exposes three orthogonal observability surfaces — metrics, logs, and traces — plus dnstap for structured query/response capture and /health and /ready endpoints for orchestrators. This page is the operator-facing overview; the underlying plugins are documented in Plugins → Observability.
Quick recipe
A practical Corefile for production:
. {
metadata
errors
log . "{remote} {name} {type} {rcode} {duration}"
cache
forward . 8.8.8.8 1.1.1.1 {
max_concurrent 1000
}
prometheus :9153
health :8080 {
lameduck 5s
}
ready :8181
pprof :6060 # optional, useful when chasing a regression
}This loads everything most operators want: structured access logs, error reporting, Prometheus metrics, an HTTP health endpoint with lameduck, an HTTP readiness endpoint, and (optional) pprof. The metadata line is what gives log access to enriched per-request values.
Metrics
The prometheus plugin exposes /metrics on its configured listener (default :9153). The metric set is documented in plugin/metrics/vars/report.go.
Core query metrics
| Metric | Type | Labels | Meaning |
|---|---|---|---|
coredns_dns_requests_total |
Counter | server, zone, view, proto, family, type |
One per request received |
coredns_dns_responses_total |
Counter | server, zone, view, rcode, plugin |
One per reply sent, attributed to the plugin that produced it |
coredns_dns_request_duration_seconds |
Histogram | server, zone, view |
Latency from arrival to reply |
coredns_dns_request_size_bytes |
Histogram | server, zone, view, proto |
Inbound DNS message size |
coredns_dns_response_size_bytes |
Histogram | server, zone, view, proto |
Outbound DNS message size |
coredns_dns_request_do_total |
Counter | server, view |
Requests with the DO bit set |
coredns_dns_request_type_total |
(deprecated, use requests_total) |
||
coredns_panics_total |
Counter | Plugin panics caught by recover() |
|
coredns_build_info |
Gauge (1) | version, revision, goversion |
Build metadata |
coredns_health_request_duration_seconds |
Histogram | Self-check latency from health |
The plugin label on responses_total is the killer feature: it tells you which plugin in the chain produced the answer. So responses_total{plugin="cache"} is your hit count and responses_total{plugin="forward"} is your miss/forward count.
Per-plugin metrics
Many plugins ship their own metrics. A subset:
| Plugin | Metrics |
|---|---|
cache |
coredns_cache_hits_total, coredns_cache_misses_total, coredns_cache_entries, coredns_cache_drops_total |
forward |
coredns_forward_request_duration_seconds, coredns_forward_responses_total{rcode}, coredns_forward_healthcheck_failures_total, coredns_forward_max_concurrent_rejects_total |
kubernetes |
coredns_kubernetes_dns_programming_duration_seconds, coredns_kubernetes_dns_endpoints_total |
acl |
coredns_acl_blocked_responses_total |
dnssec |
coredns_dnssec_cache_size, coredns_dnssec_cache_hits_total |
metadata |
(none — instead other plugins read its values) |
prometheus |
(the plugin itself; /metrics is its output) |
Each plugin's metrics file (plugin/<name>/metrics.go) is the source of truth. Bumping a metric name is a backward-incompatible change handled by the deprecation policy described in Deployment.
Useful Grafana queries
# QPS
sum(rate(coredns_dns_requests_total[1m])) by (server)
# Cache hit ratio
sum(rate(coredns_dns_responses_total{plugin="cache"}[5m]))
/ sum(rate(coredns_dns_responses_total[5m]))
# 99th-percentile request latency
histogram_quantile(0.99,
sum(rate(coredns_dns_request_duration_seconds_bucket[5m])) by (le, server))
# Forward upstream errors
sum(rate(coredns_forward_responses_total{rcode!="NOERROR"}[5m])) by (rcode)Logs
Two plugins:
logprints one line per request. Configurable format withreplacerplaceholders ({remote},{name},{type},{rcode},{rsize},{duration},{>id},{>do},{>port},{plugin}).errorsprints one line per plugin error and rate-limits identical error groups viaconsolidate.
Class filters on log (success, denial, error, all) let you log only NXDOMAINs in production.
Format strings can be JSON for ingestion into a log pipeline:
log . `{"remote":"{remote}","name":"{name}","type":"{type}","rcode":"{rcode}","duration":"{duration}"}`Tracing
The trace plugin emits OpenTracing-style spans for each query and each downstream plugin call. Two backends:
- Zipkin —
trace zipkin localhost:9411 - Datadog —
trace datadog localhost:8126
OpenTelemetry support is in progress; check the plugin README for the latest status.
A trace contains one span per plugin in the chain. If your forward plugin is suddenly slow, the trace tells you whether it's the upstream RTT, the connection pool, or one of the wrapping plugins.
dnstap
Binary structured query/response capture, framed via dnstap. Used when:
- You need exactly the bytes that hit the server (logs are derived from
dns.Msg). - You want machine-readable samples for offline analysis.
- You're correlating CoreDNS behaviour with an upstream resolver's behaviour (the
forwardplugin emitsforward_query/forward_responseevents).
dnstap unix:///var/run/dnstap.sock full
forward . 8.8.8.8full includes the message payload (otherwise only the metadata is emitted). Read the socket with dnstap's CLI tool.
Health and readiness
/health(default:8080) returns 200 OK while the process is alive. Use this for liveness probes. Withlameduck DURATIONthe endpoint stays 200 OK for the window after shutdown begins, so a load balancer can drain./ready(default:8181) returns 200 OK only when every plugin'sReadinesscheck passes. Use this for readiness probes. Thekubernetesplugin reports not-ready until the initial informer sync completes;forwardreports not-ready while every upstream is failing health checks.
pprof
pprof :6060Standard Go pprof endpoints. Useful when chasing a memory or goroutine leak in production. Profile types: profile (CPU), heap, allocs, goroutine, block, mutex. Disable in production builds where the diagnostic surface is a concern.
Putting it together
A complete monitoring deployment for a Kubernetes cluster:
| Source | Sink |
|---|---|
prometheus :9153 → /metrics |
Prometheus scrape |
log to stdout |
Container log driver → ELK/Loki |
errors to stdout |
Same |
dnstap unix://... |
dnstap collector → Kafka |
trace zipkin ... |
Zipkin/OTel collector |
health :8080 with lameduck |
livenessProbe |
ready :8181 |
readinessProbe |
Most production deployments do not need all of these. Metrics + access logs + health/ready are the minimum; trace and dnstap are situational.
Reload-safe metric semantics
When a Corefile reload happens, the metrics process keeps running (it's process-wide). Counters do not reset. Per-zone labels are added/removed as AddZone/RemoveZone are called by the new server set. Histograms inherit their accumulated buckets. The coredns_build_info gauge stays at 1; only version/revision would change after a binary swap.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.