CoreDNS in Kubernetes
Overview
CoreDNS is the default DNS server in Kubernetes. It provides:
- Service discovery via DNS
- Pod DNS resolution
- External DNS forwarding
CoreDNS Architecture
┌─────────────────────────────────────────────────────────────┐
│ CoreDNS Pod │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Corefile (ConfigMap: coredns) │ │
│ │ ├── kubernetes plugin (cluster DNS) │ │
│ │ ├── forward plugin (external DNS) │ │
│ │ └── other plugins (cache, loop, etc.) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
kube-dns Service (10.96.0.10:53)
│
┌───────────────┼───────────────┐
▼ ▼ ▼
Pod DNS Service DNS External DNSCoreDNS Components
| Component | Location |
|---|---|
| Deployment | kube-system/coredns |
| ConfigMap | kube-system/coredns |
| Service | kube-system/kube-dns |
# Check CoreDNS deployment
kubectl get deployment coredns -n kube-system
# Check CoreDNS pods
kubectl get pods -n kube-system -l k8s-app=kube-dns
# Check CoreDNS service
kubectl get svc kube-dns -n kube-systemCKA Focus: What to Change, How to Change, What Changes
What You Typically Change
| Change | Where | Why it matters (CKA) |
|---|---|---|
| Upstream DNS | forward plugin in Corefile | Fix external name resolution issues |
| Custom DNS entries | hosts or rewrite plugin | Map hostnames for debugging or internal names |
| Cluster domain | kubernetes plugin zone | Required when cluster domain is not cluster.local |
| Caching behavior | cache plugin | Reduce latency or fix stale records |
| Pod resolution mode | pods setting in kubernetes plugin | Controls pod DNS record behavior |
How to Change (Safe Flow)
- Edit the ConfigMap (Corefile).
- Ensure
reloadplugin exists (auto-reloads CoreDNS). - Validate CoreDNS pods become Ready.
- Test DNS from a pod.
# Edit the CoreDNS config
kubectl -n kube-system edit configmap coredns
# Or apply a YAML change
kubectl -n kube-system apply -f coredns.yaml
# If reload plugin is missing or you want to force it
kubectl -n kube-system rollout restart deployment coredns
# Validate pods
kubectl -n kube-system get pods -l k8s-app=kube-dnsWhat Changes When You Modify CoreDNS
| Change | Immediate effect | How to verify |
|---|---|---|
| Corefile edits | Reloaded in-place (if reload plugin present) | kubectl logs -n kube-system deployment/coredns |
| Restart CoreDNS | Short DNS outage (seconds) | kubectl get pods -n kube-system |
| Forward target change | External name resolution changes | kubectl exec ... -- nslookup google.com |
| Hosts entries | Cluster DNS responses for that name | kubectl exec ... -- nslookup custom.example.com |
CKA-Style Checklist
- Confirm DNS service IP:
kubectl get svc kube-dns -n kube-system - Confirm CoreDNS endpoints:
kubectl get endpoints kube-dns -n kube-system - Confirm pods Ready:
kubectl get pods -n kube-system -l k8s-app=kube-dns - Test within a pod using
nslookupordig
DNS Records in Kubernetes
Service DNS Names
| Type | Format |
|---|---|
| ClusterIP | <service>.<namespace>.svc.cluster.local |
| Headless | <pod-name>.<service>.<namespace>.svc.cluster.local |
| Shorthand | <service> (same namespace) or <service>.<namespace> |
Examples:
# Full FQDN
nginx-svc.default.svc.cluster.local
# Short form (same namespace)
nginx-svc
# Short form (different namespace)
nginx-svc.productionPod DNS Names
| Scenario | Format |
|---|---|
| Default | <pod-ip-dashed>.<namespace>.pod.cluster.local |
| Headless Service | <pod-name>.<service>.<namespace>.svc.cluster.local |
| hostname/subdomain | <hostname>.<subdomain>.<namespace>.svc.cluster.local |
Example:
# Pod IP: 10.244.1.5
10-244-1-5.default.pod.cluster.localCorefile Configuration
The CoreDNS configuration is stored in ConfigMap:
kubectl get configmap coredns -n kube-system -o yamlDefault 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
}Corefile Plugins
| Plugin | Purpose |
|---|---|
errors | Log errors to stdout |
health | Health check endpoint (:8080/health) |
ready | Readiness endpoint (:8181/ready) |
kubernetes | Serve DNS for cluster resources |
prometheus | Metrics endpoint (:9153/metrics) |
forward | Forward non-cluster DNS to upstream |
cache | Cache DNS responses |
loop | Detect forwarding loops |
reload | Auto-reload Corefile changes |
loadbalance | Round-robin A/AAAA records |
Pod DNS Configuration
dnsPolicy Options
| Policy | Description |
|---|---|
ClusterFirst | Use CoreDNS, fallback to node DNS (default) |
Default | Use node's DNS settings |
ClusterFirstWithHostNet | For hostNetwork pods |
None | Use custom dnsConfig only |
apiVersion: v1
kind: Pod
metadata:
name: dns-custom
spec:
dnsPolicy: ClusterFirst # default
containers:
- name: app
image: nginxCustom DNS Config
apiVersion: v1
kind: Pod
metadata:
name: custom-dns
spec:
dnsPolicy: "None"
dnsConfig:
nameservers:
- 8.8.8.8
- 8.8.4.4
searches:
- my.dns.search.suffix
options:
- name: ndots
value: "2"
containers:
- name: app
image: nginxHeadless Service DNS
For StatefulSets with headless service:
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
clusterIP: None # Headless
selector:
app: mysql
ports:
- port: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-headless # Links to headless service
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0DNS records created:
mysql-0.mysql-headless.default.svc.cluster.local
mysql-1.mysql-headless.default.svc.cluster.local
mysql-2.mysql-headless.default.svc.cluster.localCustom DNS Entries (Stub Domains)
Forward specific domains to custom DNS:
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
# ... existing config ...
forward . /etc/resolv.conf
}
# Custom domain
example.com:53 {
forward . 10.0.0.10
}Troubleshooting DNS
Test DNS from Pod
# Run a debug pod
kubectl run dnsutils --image=registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3 --restart=Never -- sleep infinity
# Test DNS resolution
kubectl exec dnsutils -- nslookup kubernetes
kubectl exec dnsutils -- nslookup nginx-svc.default.svc.cluster.local
# Check /etc/resolv.conf
kubectl exec dnsutils -- cat /etc/resolv.confCheck CoreDNS Logs
kubectl logs -n kube-system -l k8s-app=kube-dnsCheck CoreDNS Config
kubectl get configmap coredns -n kube-system -o yamlCommon Issues
| Issue | Cause | Solution |
|---|---|---|
| DNS not resolving | CoreDNS pods not running | Check kubectl get pods -n kube-system |
| Timeout | Network policy blocking | Check NetworkPolicies |
| Wrong IP returned | Stale cache | Restart CoreDNS pods |
| Pod can't resolve external | Forward plugin misconfigured | Check Corefile |
DNS Debug Commands
# Check if CoreDNS service exists
kubectl get svc kube-dns -n kube-system
# Check endpoints
kubectl get endpoints kube-dns -n kube-system
# Check pod resolv.conf
kubectl exec <pod> -- cat /etc/resolv.conf
# Full DNS test
kubectl run test --rm -it --image=busybox --restart=Never -- nslookup kubernetes.defaultModify CoreDNS
Add Custom Hosts
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health
ready
hosts {
10.0.0.1 custom.example.com
fallthrough
}
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}Apply Changes
After modifying ConfigMap:
# CoreDNS auto-reloads (reload plugin)
# Or restart pods
kubectl rollout restart deployment coredns -n kube-systemUseful Commands
# Check CoreDNS status
kubectl get pods -n kube-system -l k8s-app=kube-dns
# View Corefile
kubectl get cm coredns -n kube-system -o jsonpath='{.data.Corefile}'
# Check DNS service IP
kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}'
# Test DNS resolution
kubectl run test --rm -it --image=busybox --restart=Never -- nslookup kubernetes
# Check CoreDNS logs
kubectl logs -n kube-system deployment/coredns
# Describe CoreDNS deployment
kubectl describe deployment coredns -n kube-systemKubernetes DNS — Deep Dive Notes (CKA)
1) DNS Architecture (High-Level)
Core components
- CoreDNS: Cluster DNS server
- kubelet: Injects DNS config into Pods
- Service / Endpoints / EndpointSlices: Source of truth for DNS records
Flow
Pod → /etc/resolv.conf → CoreDNS → API Server (watch) → Answer2) Pod DNS Configuration
Typical /etc/resolv.conf
nameserver 10.96.0.10
search <ns>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5Implications
- Short names expand using
searchdomains. ndots:5means names with < 5 dots are treated as relative.- FQDNs avoid search-path latency.
3) DNS Zones
| Zone | Purpose |
|---|---|
cluster.local | Cluster root |
svc.cluster.local | Services |
pod.cluster.local | Pod IP mapping |
4) Service DNS (ClusterIP)
FQDN
<service>.<namespace>.svc.cluster.localResolution
- DNS returns Service ClusterIP.
- kube-proxy does load balancing to Pods.
Flow
Client → Service IP → kube-proxy → PodKey point: DNS does not know Pod IPs for ClusterIP services.
5) Headless Service DNS
Condition
spec:
clusterIP: NoneFQDN
<service>.<namespace>.svc.cluster.localResolution
- DNS returns Pod IPs directly (A records).
- No Service IP, no kube-proxy.
Flow
Client → DNS → Pod IP (client-side decision)6) Pod DNS via Service (Stable Identity)
Required fields
spec:
hostname: <pod-name>
subdomain: <service-name>FQDN
<pod>.<service>.<namespace>.svc.cluster.localResolution
- DNS returns that Pod IP.
- Name remains stable even if IP changes.
7) Pod IP DNS (IP-Based)
FQDN
<ip-with-dashes>.<namespace>.pod.cluster.localExample
1-2-3-4.kube-system.pod.cluster.local → 1.2.3.4Characteristics
- DNS-only transformation.
- Rare in production, but tested in CKA/CKS.
8) EndpointSlices and DNS
CoreDNS watches:
- Services
- EndpointSlices (preferred)
- Endpoints (legacy)
- Pods (for Pod DNS)
Mapping
- ClusterIP Service → ClusterIP
- Headless Service → EndpointSlice addresses
- Pod DNS → Pod metadata
9) SRV Records (Named Ports)
_<port-name>._<protocol>.<service>.<namespace>.svc.cluster.localExample
_http._tcp.department.lima-workload.svc.cluster.localReturns port + target hostname, used by service discovery-aware apps.
10) Failure Modes & Debugging
Commands
nslookup <fqdn>
dig <fqdn>
kubectl get svc
kubectl get endpointslice
kubectl describe svcCommon issues
- Service selector mismatch
- No endpoints (DNS exists, but no A records)
- Wrong namespace
- Missing
hostname/subdomainfor Pod DNS
11) CoreDNS — Deep Dive
What it is
- DNS server in Go
- Deployment (usually 2 replicas)
- Runs in
kube-system
Data sources: Watches Services, EndpointSlices, Pods, Namespaces (event-driven).
Typical Corefile
.:53 {
errors
health
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
forward . /etc/resolv.conf
cache 30
loop
reload
}pods insecure meaning
Allows *.pod.cluster.local without verifying Pod existence.
12) DNS Resolution Decision Tree (Mental Model)
Is it *.svc.cluster.local?
├─ Service with ClusterIP → Service IP
└─ Service with clusterIP: None → Pod IPs
Is it <pod>.<svc>.<ns>.svc.cluster.local?
└─ Pod hostname + subdomain → Pod IP
Is it *.pod.cluster.local?
└─ IP reconstruction → Literal IP13) CKA Exam Cheat Sheet
- Prefer FQDN in tests.
- Headless Service → Pod IPs.
- Service DNS → Service IP.
- Stable Pod DNS →
hostname + subdomain. .pod.cluster.local→ DNS-only IP mapping.- CoreDNS is the primary DNS component to debug.
14) Key Takeaway
Kubernetes DNS is data-driven:
- CoreDNS answers using API objects.
- Services abstract Pods unless headless.
- Pod identity is opt-in.
- kube-proxy is only involved for Service IPs.