1. What CoreDNS Is¶
CoreDNS is a general-purpose, plugin-based DNS server written in Go, built around a single core idea: DNS behavior is assembled from a chain of plugins, each doing one job (serving zone data, forwarding, caching, rewriting, exposing metrics), rather than one monolithic daemon with a fixed feature set. It's a CNCF graduated project, and its most consequential deployment by far is as the default in-cluster DNS server for Kubernetes since v1.13 (replacing kube-dns) — if you run Kubernetes, you are almost certainly already running CoreDNS, whether you've configured it directly or not.
Where BIND and Unbound are configured with dedicated DSLs (named.conf, unbound.conf), CoreDNS is configured with a Corefile, and its behavior is entirely determined by which plugins are enabled and in what order — there's no fixed "authoritative vs. recursive" mode; a server block with the file plugin is authoritative, one with forward is a forwarder, and one with both does both, per zone.
2. The Corefile¶
. {
forward . 8.8.8.8 8.8.4.4
cache 30
log
errors
}
Structure: one or more server blocks, each headed by the zone(s) and port it serves, containing an ordered list of plugin directives. . means "the root zone" — i.e., "everything," the same convention as BIND's zone "." hint zone or Unbound's forward-zone name: ".".
Multiple server blocks can coexist, each scoped to different zones:
example.local:53 {
file /etc/coredns/db.example.local
log
}
. {
forward . /etc/resolv.conf
cache
}
Here, example.local is served authoritatively from a zone file, while everything else is forwarded to whatever resolvers the host itself uses.
3. Core Plugins¶
| Plugin | Purpose |
|---|---|
forward |
Forward queries to upstream resolvers, with health-checked failover across multiple targets |
file |
Serve a zone from a standard BIND-format zone file — authoritative serving |
auto |
Like file, but automatically reloads zone files from a directory on change, no restart needed |
cache |
Cache responses; argument is TTL cap in seconds |
kubernetes |
Serve DNS records for Kubernetes Services and Pods directly from the API server's state — this is what makes CoreDNS the Kubernetes DNS server |
hosts |
Serve entries from an /etc/hosts-style file, similar in spirit to dnsmasq's --addn-hosts |
rewrite |
Rewrite query names/types/classes before further processing — used heavily for Kubernetes' internal name-mangling needs |
log / errors |
Structured logging of queries and errors |
metrics (prometheus) |
Expose Prometheus-format metrics on a /metrics endpoint — first-class observability, unlike BIND/Unbound where metrics require extra scraping tooling |
health |
Liveness/readiness HTTP endpoint, used directly by Kubernetes' own liveness probes for the CoreDNS pod itself |
autopath |
Optimizes Kubernetes' search-domain expansion, avoiding repeated NXDOMAIN round-trips for each search suffix tried |
dnssec |
On-the-fly DNSSEC signing for authoritative answers |
4. CoreDNS in Kubernetes¶
The most likely place you'll actually touch CoreDNS is its ConfigMap in a cluster:
kubectl -n kube-system get configmap coredns -o yaml
A typical cluster Corefile:
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
Walking this: kubernetes handles the cluster's internal namespace (cluster.local) and reverse zones, resolving Service/Pod names against live API state rather than a static zone file — a Service's ClusterIP DNS entry appears the moment the Service object exists, with no file to edit, the cluster-scale analog of dnsmasq's DHCP-to-DNS integration. Anything CoreDNS doesn't recognize as an in-cluster name falls through (fallthrough) to the forward plugin, which sends it to the node's own upstream resolvers. loop detects and breaks accidental forwarding loops (a real risk if a misconfigured cluster forwards back to itself). reload watches the Corefile/ConfigMap for changes and reloads without a pod restart.
Editing cluster DNS behavior (adding a custom stub domain, changing upstream forwarders, adding a rewrite rule for a legacy hostname) means editing this ConfigMap and letting reload pick it up, or restarting the CoreDNS deployment if reload isn't present.
5. Standalone / Non-Kubernetes Use¶
CoreDNS works perfectly well outside Kubernetes as a general authoritative or forwarding server — a legitimate BIND/Unbound alternative when you want plugin composability, first-class Prometheus metrics, or Go-based extensibility (writing a custom plugin is a supported, documented path, unlike patching BIND or Unbound's C codebases).
# Download a release binary, or:
docker run -p 53:53/udp -v $(pwd)/Corefile:/Corefile coredns/coredns
A small authoritative example equivalent to a BIND master zone:
example.local:53 {
file /etc/coredns/db.example.local
log
errors
}
with a standard zone file (file plugin reads ordinary BIND-format zone files — SOA, NS, A, MX records and all, no CoreDNS-specific syntax needed).
6. Reloading and Zero-Downtime Changes¶
Two different mechanisms matter here:
- reload plugin — watches the Corefile itself for changes and hot-reloads CoreDNS's configuration without dropping the listening socket.
- auto plugin — for zone data specifically, watches a directory of zone files and reloads any that change, independent of the Corefile changing at all.
Without either, a CoreDNS restart (brief, since it's a fast-starting Go binary) is the fallback — in Kubernetes this is a rolling pod restart of the CoreDNS Deployment, non-disruptive as long as more than one replica is running (the default).
7. Observability¶
Unlike BIND/Unbound, where "statistics" means parsing log lines or querying a control socket, CoreDNS's prometheus plugin exposes counters and histograms (query counts by zone/rcode/type, forward latency, cache hit ratio) directly as scrapeable Prometheus metrics — a natural fit if the rest of your stack (as in a homelab or Kubernetes environment) is already Prometheus/Grafana-based, since there's no separate exporter to run.
8. CoreDNS vs. BIND/Unbound/dnsmasq¶
| CoreDNS | BIND | Unbound | dnsmasq | |
|---|---|---|---|---|
| Config model | Corefile, plugin chain | named.conf |
unbound.conf |
dnsmasq.conf |
| Extensibility | Native Go plugins | Limited (C source patches) | Limited | Limited |
| Kubernetes-native | Yes — the default | No | No | No |
| Metrics | First-class Prometheus | External tooling needed | unbound-control stats |
Minimal |
| Best fit | Kubernetes, cloud-native, programmable DNS | Authoritative hosting at scale | LAN/validating resolver | Small LAN + DHCP |
9. Reference Links¶
- CoreDNS project: https://coredns.io/
- Plugin index: https://coredns.io/plugins/
- Kubernetes DNS documentation: https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/