Qingular

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

← Back to CKA Practice Index

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:

FormatDescription
<service>Within the same namespace, just use the Service name
<service>.<namespace>Cross-namespace access
<service>.<namespace>.svcSpecify the svc subdomain
<service>.<namespace>.svc.cluster.localFull FQDN
<pod-ip>.<namespace>.pod.cluster.localPod 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/DirectiveDescription
errorsLog DNS error messages
healthHealth check endpoint :8080/health
readyReadiness check endpoint :8181/ready
kubernetesCore Kubernetes DNS resolution plugin
prometheusMetrics exposure on :9153/metrics
forwardUpstream DNS forwarding (. means all domains)
cacheDNS caching
loopDetect DNS forwarding loops
reloadAuto-reload configuration changes
loadbalanceRound-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"]
PolicyDescription
ClusterFirstDefault policy: query CoreDNS first, forward to upstream DNS on miss
DefaultPod inherits the node's DNS configuration (/etc/resolv.conf)
NoneIgnore default DNS, use dnsConfig for full customization
ClusterFirstWithHostNetFor 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

ItemCommand
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 domainkubectl get configmap -n kube-system coredns -o yaml
CoreDNS memory/CPUkubectl 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