kubernetes-coredns
title: CoreDNS tags:
- CKA
- k8s
- Practice description: CKA Domain 3 — CoreDNS in Kubernetes: role, configuration, and troubleshooting date: 2026-05-27T00:00:00.000Z
Overview
CoreDNS is the default DNS component of a Kubernetes cluster (replacing kube-dns since K8s v1.13), responsible for providing service discovery within the cluster. It provides DNS resolution for Pods and Services, enabling communication via domain names instead of IP addresses.
Pod DNS Resolution Flow
Pod → <service>.<namespace>.svc.cluster.local → CoreDNS → Service ClusterIP
Full Domain Name Format:
| Format | Description |
|---|---|
<service> | Within the same namespace, just use the Service name |
<service>.<namespace> | Cross-namespace access |
<service>.<namespace>.svc | Specify the svc subdomain |
<service>.<namespace>.svc.cluster.local | Full FQDN |
<pod-ip>.<namespace>.pod.cluster.local | Pod DNS record (not enabled by default) |
# Verify DNS resolution
kubectl run test-dns --image=busybox:1.28 --rm -it --restart=Never -- nslookup kubernetes.default.svc.cluster.local
# Or use nslookup to resolve a Service in the same namespace
kubectl run test-dns --image=busybox:1.28 --rm -it --restart=Never -- nslookup my-svc
Pod DNS Records
# Pod DNS name (if pod records are enabled)
<POD-IP-WITH-DASHES>.<NAMESPACE>.pod.cluster.local
# Example: Pod with IP 10.244.1.5
10-244-1-5.default.pod.cluster.local
CoreDNS ConfigMap Configuration
CoreDNS configuration is stored in a ConfigMap in the kube-system namespace:
kubectl get configmap -n kube-system coredns -o yaml
Default Configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
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
cache 30
loop
reload
loadbalance
}
Configuration Directive Reference
| Plugin/Directive | Description |
|---|---|
errors | Log DNS error messages |
health | Health check endpoint :8080/health |
ready | Readiness check endpoint :8181/ready |
kubernetes | Core Kubernetes DNS resolution plugin |
prometheus | Metrics exposure on :9153/metrics |
forward | Upstream DNS forwarding (. means all domains) |
cache | DNS caching |
loop | Detect DNS forwarding loops |
reload | Auto-reload configuration changes |
loadbalance | Round-robin load balancing for A/AAAA records |
Custom DNS Configuration
Adding Custom stubDomains
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
# Custom domain resolution
example.com {
forward . 192.168.1.1
}
# Internal private domain
internal.company.com {
forward . 10.0.0.10 10.0.0.11
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
Pod dnsPolicy
Defines the DNS policy for a Pod:
apiVersion: v1
kind: Pod
metadata:
name: dns-test-pod
spec:
dnsPolicy: ClusterFirst # Default value
containers:
- name: busybox
image: busybox:1.28
command: ["sleep", "3600"]
| Policy | Description |
|---|---|
ClusterFirst | Default policy: query CoreDNS first, forward to upstream DNS on miss |
Default | Pod inherits the node's DNS configuration (/etc/resolv.conf) |
None | Ignore default DNS, use dnsConfig for full customization |
ClusterFirstWithHostNet | For Pods with hostNetwork: true |
Pod dnsConfig Customization
When dnsPolicy: None, use dnsConfig for full customization:
apiVersion: v1
kind: Pod
metadata:
name: custom-dns-pod
spec:
dnsPolicy: None
dnsConfig:
nameservers:
- 1.1.1.1
- 8.8.8.8
searches:
- default.svc.cluster.local
- svc.cluster.local
- cluster.local
options:
- name: ndots
value: "2"
- name: edns0
containers:
- name: busybox
image: busybox:1.28
command: ["sleep", "3600"]
CoreDNS Troubleshooting
# Check CoreDNS Pod status
kubectl get pods -n kube-system -l k8s-app=kube-dns
# View CoreDNS logs
kubectl logs -n kube-system -l k8s-app=kube-dns
# View CoreDNS ConfigMap
kubectl get configmap -n kube-system coredns -o yaml
# Restart CoreDNS (after modifying configuration)
kubectl rollout restart -n kube-system deployment/coredns
# Check CoreDNS Service
kubectl get svc -n kube-system kube-dns
# Check CoreDNS resource usage
kubectl top pods -n kube-system -l k8s-app=kube-dns
Common Troubleshooting Procedures
# 1. Test DNS resolution
kubectl run test --image=busybox:1.28 --rm -it --restart=Never -- nslookup kubernetes.default
# 2. If nslookup fails, test whether CoreDNS is reachable
kubectl run test --image=busybox:1.28 --rm -it --restart=Never -- wget http://<coredns-svc-ip>:8080/health
# 3. Check DNS timeout
kubectl run test --image=busybox:1.28 --rm -it --restart=Never -- sh
# Inside the container:
cat /etc/resolv.conf
# Output similar to:
# nameserver 10.96.0.10
# search default.svc.cluster.local svc.cluster.local cluster.local
# options ndots:5
# 4. Full-chain DNS test (from inside a Pod)
kubectl run dig-pod --image=nicolaka/netshoot --rm -it --restart=Never -- dig kubernetes.default.svc.cluster.local
Key Troubleshooting Items
| Item | Command |
|---|---|
| Are CoreDNS Pods running? | kubectl get pods -n kube-system -l k8s-app=kube-dns |
| Are Service Endpoints healthy? | kubectl get endpoints -n kube-system kube-dns |
| Is DNS configuration correct? | kubectl exec <pod> -- cat /etc/resolv.conf |
| Cluster DNS domain | kubectl get configmap -n kube-system coredns -o yaml |
| CoreDNS memory/CPU | kubectl top pods -n kube-system -l k8s-app=kube-dns |
🧪 Complete Hands-on Example: Testing Pod DNS Resolution and Customizing CoreDNS
Scenario
Verify that cluster DNS resolution is working properly, then customize CoreDNS by modifying its ConfigMap to add custom stubDomains so that internal private domain names can be resolved within the cluster.
Prerequisites
- Cluster is running normally, CoreDNS Pods are in Running state
- kubectl is configured with cluster access
Steps
Step 1: Verify Basic DNS Resolution
# Use busybox to test DNS resolution for a Kubernetes Service
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup kubernetes.default
# Expected output:
# Server: 10.96.0.10
# Address: 10.96.0.10:53
# Name: kubernetes.default.svc.cluster.local
# Address: 10.96.0.1
# Test the full FQDN
kubectl run dns-test2 --image=busybox:1.28 --rm -it --restart=Never -- nslookup kubernetes.default.svc.cluster.local
# Expected output: same as above, showing ClusterIP 10.96.0.1
# Check the Pod's DNS configuration
kubectl run check-dns --image=busybox:1.28 --rm -it --restart=Never -- cat /etc/resolv.conf
# Expected output:
# nameserver 10.96.0.10
# search default.svc.cluster.local svc.cluster.local cluster.local
# options ndots:5
Step 2: Check CoreDNS Component Status
# View CoreDNS Pods
kubectl get pods -n kube-system -l k8s-app=kube-dns
# NAME READY STATUS RESTARTS AGE
# coredns-7d8f9c7b8c-abc12 1/1 Running 0 10m
# coredns-7d8f9c7b8c-def34 1/1 Running 0 10m
# View the CoreDNS Service
kubectl get svc -n kube-system kube-dns
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 10m
# View the current CoreDNS ConfigMap configuration
kubectl get configmap -n kube-system coredns -o yaml
# Expected output: includes the Corefile configuration section
Step 3: Create Custom stubDomains
# Back up the current configuration
kubectl get configmap -n kube-system coredns -o yaml > coredns-backup.yaml
# Modify the ConfigMap to add custom domain resolution
kubectl edit configmap -n kube-system coredns
# In the editor, modify the Corefile to add the following section:
# Modified Corefile (add below the kubernetes block and above the forward block):
# Custom internal domain
internal.example.com {
forward . 192.168.1.100
}
# Custom stub domain
mycompany.local {
forward . 10.0.0.53
}
# Or use kubectl patch directly:
cat <<EOFPATCH | kubectl patch configmap -n kube-system coredns --type merge -p "$(cat)"
data:
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
}
internal.example.com {
forward . 192.168.1.100
}
mycompany.local {
forward . 10.0.0.53
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
EOFPATCH
# Expected output: configmap/coredns patched
Step 4: Restart CoreDNS to Apply the Configuration
# Check if the reload plugin takes effect (wait for auto-reload, up to 30 seconds)
# Or manually restart to force it to take effect
kubectl rollout restart -n kube-system deployment/coredns
# Expected output: deployment.apps/coredns restarted
# Wait for CoreDNS Pods to become ready again
kubectl rollout status -n kube-system deployment/coredns
# Expected output: deployment "coredns" successfully rolled out
# Verify the new Pods are ready
kubectl get pods -n kube-system -l k8s-app=kube-dns
# Expected output: new Pods, status Running, RESTARTS column 0 or 1
Step 5: Verify Custom Domain Resolution
# Test custom internal.example.com resolution
kubectl run test-stub --image=busybox:1.28 --rm -it --restart=Never -- nslookup internal.example.com
# Expected output:
# Server: 10.96.0.10
# Address: 10.96.0.10:53
# Name: internal.example.com
# Address: 192.168.1.100
# Test mycompany.local resolution
kubectl run test-stub2 --image=busybox:1.28 --rm -it --restart=Never -- nslookup mycompany.local
# Expected output:
# Server: 10.96.0.10
# Address: 10.96.0.10:53
# Name: mycompany.local
# Address: 10.0.0.53
# Confirm native K8s DNS resolution still works
kubectl run test-k8s --image=busybox:1.28 --rm -it --restart=Never -- nslookup kubernetes.default
# Expected output: successfully resolves to 10.96.0.1
Verification
# View the full DNS resolution chain from inside a Pod
kubectl run dig-pod --image=nicolaka/netshoot --rm -it --restart=Never -- dig internal.example.com +trace
# Expected output: DNS resolution chain from root domain to final IP
# View CoreDNS logs to confirm custom domain queries are being handled correctly
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=20 | grep "internal.example.com"
# Expected output: query logs shown (if the errors plugin is enabled)
# Clean up test resources
kubectl delete pod dig-pod --force --grace-period=0 2>/dev/null || true
# Restore the original CoreDNS configuration (if backed up)
# kubectl apply -f coredns-backup.yaml
# kubectl rollout restart -n kube-system deployment/coredns
Exam Tips
-
CoreDNS ConfigMap is in the kube-system namespace, named
coredns -
After modifying the configuration, you must restart CoreDNS (
kubectl rollout restart) or wait for the reload to take effect automatically -
The
.:53block is the default server block, handling all unmatched domains; custom blocks take priority for matched domains -
Forward order matters: the forward directive within a custom block only applies to that domain, while the
.block's forward handles all other domains -
Four-step DNS troubleshooting: ①
kubectl get pods -n kube-system -l k8s-app=kube-dns②kubectl logs③ Check ConfigMap ④ Use netshoot/dig for layer-by-layer troubleshooting -
busybox:1.28's nslookup is the most stable; newer versions of busybox may lack the nslookup command -
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/
-
https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/
-
https://kubernetes.io/docs/tasks/administer-cluster/coredns/