CoreDNS is a fast, flexible, and extensible DNS server written in Go that is most famous for being the default cluster DNS for Kubernetes. It functions by chaining together a series of plugins, where each plugin performs a specific DNS action like caching, service discovery, or rewriting queries. The CoreDNS project is hosted by the CNCF.
Its main job is:
- Convert service names → IP addresses
- Help containers/pods discover each other
- Provide internal cluster networking resolution
- Optionally forward external DNS requests to upstream DNS servers
Example:
A pod wants to connect to:
mysql.default.svc.cluster.local
CoreDNS answers:
10.96.12.7
So the pod can connect without knowing the actual IP.
Why CoreDNS Exists
In Kubernetes:
- Pods are created/destroyed constantly
- IPs change frequently
- Hardcoding IP addresses is impossible
CoreDNS creates a dynamic DNS system for the cluster.
High-Level Architecture
+-------------------+
| Application Pod |
| nginx pod |
+---------+---------+
|
| DNS Query
v
+-------------------+
| /etc/resolv.conf |
| nameserver x.x.x.x|
+---------+---------+
|
v
+-------------------+
| CoreDNS Service |
| kube-dns ClusterIP|
+---------+---------+
|
v
+-------------------+
| CoreDNS Pods |
| DNS Engine |
+---------+---------+
|
+------+------+
| |
v v
Kubernetes External DNS
API Server (8.8.8.8)
What Happens Behind the Scenes
Suppose this pod runs:
curl http://backend-service
Step 1 — Application Makes DNS Request
The Linux resolver library checks:
/etc/resolv.conf
Inside pod:
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local
10.96.0.10 is the CoreDNS service IP.
Step 2 — Packet Goes to CoreDNS Service
The DNS request travels through:
- veth pair
- container namespace
- CNI network
- kube-proxy/IPVS/iptables
Eventually reaches CoreDNS pod.
Step 3 — CoreDNS Receives Query
Query example:
backend-service.default.svc.cluster.local
CoreDNS parses the request.
CoreDNS Internal Pipeline
CoreDNS uses a plugin architecture.
Request flow:
DNS Request
|
v
+------------+
| log plugin |
+------------+
|
+------------+
| cache |
+------------+
|
+------------+
| kubernetes |
+------------+
|
+------------+
| forward |
+------------+
Each plugin processes the request.
Kubernetes Plugin
This is the important part.
CoreDNS queries the Kubernetes API:
GET /api/v1/services
GET /api/v1/endpoints
It keeps an internal cache of:
- Services
- Pods
- Endpoints
- Namespaces
Internal Data Flow
+------------------+
| Kubernetes API |
+--------+---------+
^
| Watch API
|
+--------+---------+
| CoreDNS |
| Internal Cache |
+--------+---------+
^
|
DNS Query|
|
+--------+---------+
| Application Pod |
+------------------+
CoreDNS watches the API continuously.
When a service changes:
- Kubernetes notifies CoreDNS
- Cache updates immediately
No database lookup is needed for every query.
Example Resolution
Service:
apiVersion: v1
kind: Service
metadata:
name: backend
Cluster IP:
10.96.20.15
CoreDNS stores:
backend.default.svc.cluster.local
->
10.96.20.15
When queried:
Q: backend.default.svc.cluster.local
A: 10.96.20.15
External DNS Requests
If query is NOT cluster-local:
Example:
google.com
CoreDNS forwards the request to:
-
/etc/resolv.conf - upstream DNS
- 8.8.8.8
- corporate DNS
Using the forward plugin.
Corefile (Configuration)
CoreDNS config:
.:53 {
errors
health
kubernetes cluster.local
forward . 8.8.8.8
cache 30
}
Meaning:
| Plugin | Purpose |
| ---------- | --------------- |
| errors | log errors |
| health | health endpoint |
| kubernetes | cluster DNS |
| forward | external DNS |
| cache | cache responses |
Deep Internal Architecture
+----------------+
| Kubernetes API |
+--------+-------+
^
|
Watch/Informer
|
+------------------------------------------------+
| CoreDNS Pod |
| |
| +------------------------------------------+ |
| | DNS Listener :53 | |
| +-------------------+----------------------+ |
| | |
| Plugin Chain |
| | |
| +--------+--------+--------+--------+ |
| | cache | kubernetes | forward | log| |
| +--------+--------+--------+--------+ |
| | |
| Internal Memory Cache |
+------------------------------------------------+
How CoreDNS Scales
Usually, Kubernetes runs:
Deployment/coredns
with multiple replicas.
Example:
coredns-abc
coredns-def
The service load-balances requests.
Performance Optimizations
CoreDNS uses:
- In-memory cache
- Kubernetes informers/watch
- UDP for fast queries
- Minimal plugin chain
- Concurrent request handling
Failure Scenario
If CoreDNS dies:
- Pods cannot resolve service names
- Internal networking breaks
- Existing TCP connections still work
This is why production clusters run multiple replicas.
Important Kubernetes DNS Names
| Type | Example |
| ---------------- | ------------------------------------ |
| Service | mysql.default.svc.cluster.local |
| Pod | 10-1-2-3.default.pod.cluster.local |
| Namespace search | mysql → auto-expanded |
If you wanted to add a private IP
If you want to add a custom private IP/domain mapping in CoreDNS, there are several ways depending on your goal.
Common example:
db.internal.local -> 192.168.10.50
or:
gitlab.home.lab -> 10.0.0.15
Easiest way Using hosts plugin.
Step 1 — Edit CoreDNS ConfigMap
Open CoreDNS config:
kubectl -n kube-system edit configmap coredns
You will see:
Corefile:
Step 2 — Add hosts Plugin
Example:
.:53 {
errors
health hosts {
192.168.10.50 db.internal.local
10.0.0.15 gitlab.home.lab
fallthrough
} kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
} forward . /etc/resolv.conf
cache 30
}
What Happens Internally
DNS Query
|
v
+-----------+
| hosts | <-- checks static records
+-----------+
|
+--> Match found?
|
+--> Return private IP
|
+--> Otherwise continue
fallthrough means:
“If no match found, continue to the next plugin.”
Step 3 — Restart CoreDNS
Usually automatic, but safest:
kubectl rollout restart deployment coredns -n kube-system
Step 4 — Test
Inside pod:
nslookup db.internal.local
Result:
Name: db.internal.local
Address: 192.168.10.50
Architecture Behind It
Pod
|
| DNS Query
v
CoreDNS
|
+--> hosts plugin
| |
| +--> static memory lookup
|
+--> kubernetes plugin
|
+--> forward plugin
Real Networking Flow
Pod A
|
| DNS Query
v
CoreDNS Service IP
|
v
CoreDNS Pod
|
+--> Kubernetes Cache
|
+--> Upstream DNS
|
v
DNS Response
|
v
Pod A connects to target
CoreDNS is the default DNS server in Kubernetes clusters that provides dynamic service discovery between pods and services. Instead of relying on fixed IP addresses, applications communicate using DNS names such as mysql.default.svc.cluster.local, which CoreDNS resolves into cluster IPs.
Behind the scenes, CoreDNS acts as a plugin-based DNS engine that continuously monitors the Kubernetes API server for changes to services, pods, and endpoints. It maintains an in-memory cache of cluster resources, allowing fast DNS resolution without querying the API on every request. Requests pass through a plugin pipeline that can include caching, Kubernetes service discovery, logging, and forwarding to external DNS servers.
The blog also explains how to add custom private IP mappings into CoreDNS using multiple methods. The simplest approach uses the hosts plugin to statically map internal domains to private IP addresses. For larger environments, administrators can create custom DNS zones or use Kubernetes-native Service and Endpoints Resources to expose external systems through cluster DNS.
Overall, CoreDNS acts as the networking discovery layer of Kubernetes, combining DNS resolution, Kubernetes API integration, caching, and plugin extensibility to make container communication reliable and dynamic.

Top comments (0)