DNS Best Practices
Cluster DNS is quiet until it is not, and a DNS slowdown looks like a slowdown everywhere.
These practices keep name resolution fast, predictable, and portable across environments.
How to Use This List
Skim the groups and adopt the ones that fit your workloads.
Treat the naming and FQDN rules as defaults for new services, and the scaling and caching rules as things to verify under load.
Each bullet leads with the action so you can turn it into a review checklist.
A - Naming and Name Resolution
- Use short names within a namespace. Inside one namespace, call
payments, not the full FQDN, to keep manifests readable and portable. - Use FQDNs in polyglot or cross-namespace config. Different language resolvers treat the
searchlist andndotsdifferently, sopayments.billing.svc.cluster.localbehaves identically everywhere. - Add a trailing dot for known-absolute names. Writing
api.github.com.marks the name absolute and skips search-suffix attempts entirely. - Never hardcode pod IPs. Pods are ephemeral; target the Service name so connections survive rescheduling and rollouts.
- Scope names by namespace deliberately. Rely on the fact that a bare name resolves in-namespace to keep environments isolated.
B - Performance and Caching
- Lower ndots for external-heavy workloads. The default
ndots:5causes several failed lookups before an external FQDN resolves; setdnsConfigndots:2or use FQDNs. - Deploy NodeLocal DNSCache. A per-node caching agent cuts DNS latency and reduces conntrack pressure from UDP DNS across nodes.
- Keep a sensible cache TTL. The CoreDNS
cacheplugin (for examplecache 30) shields upstreams and the API server without serving badly stale answers. - Reuse connections in clients. Connection pooling and HTTP keep-alive reduce how often clients resolve names in the first place.
- Watch DNS latency as an SLI. Alert on CoreDNS query latency and error rate; DNS regressions surface as broad, confusing failures.
C - CoreDNS Scaling and Reliability
- Scale CoreDNS for the load. Two replicas is a floor; add replicas or an autoscaler for DNS-heavy clusters and large node counts.
- Enable the loop plugin. It detects forwarding loops at startup so a misconfigured upstream fails fast instead of crashing repeatedly.
- Use explicit upstream forwards. Forward to specific resolver IPs rather than inheriting the node
resolv.confto avoid surprise loops and drift. - Back readiness with health and ready plugins. These ensure a broken CoreDNS pod is pulled from rotation instead of blackholing queries.
- Roll out Corefile changes carefully. Validate edits, watch logs, and rely on the
reloadplugin; a malformed Corefile silently keeps stale config.
D - Naming for Stateful and External Workloads
- Use headless Services for stable pod identity. StatefulSets need
clusterIP: Noneso each pod gets a name likedb-0.db.data.svc.cluster.local. - Prefer ClusterIP for load-balanced traffic. Reserve headless mode for genuine per-pod addressing, since headless answers change as pods churn.
- Alias external dependencies with ExternalName. Give managed databases and third-party APIs a stable internal name you can repoint per environment.
- Use a headless Service plus manual EndpointSlice for IP-only targets. ExternalName needs a DNS name; a manual EndpointSlice handles a raw external IP.
- Mind TLS hostnames for external calls. The client connects to the resolved external hostname, so certificates must match that name, not the alias.
E - Security and Isolation
- Do not treat DNS as an access control boundary. Resolution and traffic policy are separate; enforce reachability with NetworkPolicies.
- Apply a default-deny NetworkPolicy, then allow DNS. Explicitly permit egress to the
kube-dnsService on UDP/TCP 53 so denied namespaces can still resolve. - Isolate private zones with stub domains. Route internal domains to corporate resolvers in their own CoreDNS server block instead of the general upstream.
- Avoid leaking internal names publicly. Keep split-horizon views separate so cluster-internal names are not served to external clients.
- Log and monitor DNS anomalies. Sudden NXDOMAIN spikes or query floods can signal misconfiguration or abuse.
When You Are Done
Confirm new services use short names in-namespace and FQDNs in shared config.
Verify CoreDNS is scaled, has loop, health, and cache configured, and that a default-deny NetworkPolicy still allows port 53 egress.
Check that external dependencies use ExternalName or a manual EndpointSlice, and that DNS latency is on your dashboards.
FAQs
Short name or FQDN by default?
Short names within a namespace for readability; FQDNs in cross-namespace or polyglot config for consistent resolution.
How do I stop slow external lookups?
Lower ndots, use FQDNs with a trailing dot, and deploy NodeLocal DNSCache.
How many CoreDNS replicas should I run?
At least two, scaled up for large clusters or DNS-heavy workloads; measure query latency to size it.
Does a NetworkPolicy block DNS?
A default-deny egress policy will unless you explicitly allow egress to kube-dns on port 53.
Why does my StatefulSet need a headless Service?
Headless Services publish per-pod DNS names, which stateful peers rely on for stable identity.
Is ExternalName secure by itself?
No; it is DNS indirection only. Enforce control with NetworkPolicies or an egress gateway.
Related
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).