CoreDNS is a DNS server that’s often used in Kubernetes, but BIND is another popular DNS server.

Let’s see how they handle a common DNS query. Imagine a pod in Kubernetes needs to resolve my-service.my-namespace.svc.cluster.local.

Here’s what happens with CoreDNS:

# On a Kubernetes node, simulating a pod's DNS request
kubectl exec -it <some-pod-name> -- nslookup my-service.my-namespace.svc.cluster.local

# CoreDNS will receive this query. Its configuration (often a ConfigMap)
# will tell it how to handle internal Kubernetes DNS.
# For example, a simplified Corefile might look like this:

.:53 {
    errors
    health {
       lameduck 5s
    }
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
       pods insecure
       upstream
       fallthrough in-addr.arpa ip6.arpa
    }
    prometheus :9153
    forward . /etc/resolv.conf
    cache 30
    loop
    reload
    loadbalance
}

CoreDNS, seeing the kubernetes plugin, recognizes my-service.my-namespace.svc.cluster.local as a cluster-internal domain. It queries the Kubernetes API server for the endpoints associated with my-service in my-namespace and returns the ClusterIP or NodePorts.

Now, let’s consider BIND. If you were to use BIND inside Kubernetes (which is less common for in-cluster DNS but possible for external DNS), it would be configured differently.

A typical BIND named.conf might look like:

options {
    directory "/var/cache/bind";
    recursion yes;
    allow-query { any; }; // For simplicity, usually more restricted
};

zone "cluster.local" IN {
    type master;
    file "/etc/bind/db.cluster.local";
};

// ... other zones and forwarders ...

And the db.cluster.local file would manually list A and SRV records for services.

$TTL    60
@       IN      SOA     ns1.cluster.local. admin.cluster.local. (
                        2023102701 ; serial
                        3600       ; refresh
                        1800       ; retry
                        604800     ; expire
                        60 )       ; minimum TTL
;
@       IN      NS      ns1.cluster.local.
ns1     IN      A       10.0.0.53  ; IP of the BIND server

my-service IN A 10.0.1.10

BIND would look up my-service.cluster.local in its db.cluster.local file and return the hardcoded IP 10.0.1.10.

The fundamental difference is how they discover and serve cluster-internal information. CoreDNS integrates directly with the Kubernetes API, making it dynamic. BIND relies on static configuration files that need manual updates as your cluster services change.

CoreDNS’s architecture is plugin-based. This allows for great flexibility. You can swap out plugins, add new ones, or configure existing ones to tailor DNS behavior. For instance, the kubernetes plugin is specifically designed to translate Kubernetes DNS names into IP addresses by talking to the API. The forward plugin tells CoreDNS where to send queries it doesn’t handle itself (like external domains).

BIND, on the other hand, is a long-standing, feature-rich DNS server. It’s incredibly powerful for managing large, complex DNS zones, especially in traditional on-premises environments. Its strength lies in its robust zone file management, extensive logging, and sophisticated security features. However, it lacks the built-in integration with dynamic orchestration platforms like Kubernetes that CoreDNS offers.

When CoreDNS encounters a query for my-service.my-namespace.svc.cluster.local, it doesn’t just look up a record. The kubernetes plugin intercepts this, queries the Kubernetes API for the Service object named my-service in my-namespace, and then constructs the DNS response based on the clusterIP and ports defined for that service. If it’s an SRV record query for a headless service, it will return the IPs of the backing pods.

If CoreDNS doesn’t recognize the domain as cluster-internal (e.g., it’s google.com), the forward plugin kicks in. It consults /etc/resolv.conf on the node where CoreDNS is running, finds the upstream DNS servers (often your cloud provider’s DNS or your internal network DNS), and forwards the query there.

The cache plugin, with a TTL of 30 seconds, stores recent DNS responses. This significantly speeds up subsequent queries for the same name by avoiding a round trip to the API server or upstream resolvers.

The reload plugin automatically watches for changes to the CoreDNS configuration (typically stored in a Kubernetes ConfigMap) and reloads the server without dropping existing connections. This means you can update your DNS setup and have it take effect almost instantly.

The loadbalance plugin is interesting. It’s often used with the kubernetes plugin to provide round-robin load balancing across multiple A records for a service if multiple IPs are returned.

The primary reason CoreDNS became the default in Kubernetes is its native understanding of the Kubernetes API and its extensibility. It’s designed to be a service discovery mechanism as much as a DNS server. BIND, while capable, requires significant external tooling to achieve the same level of dynamic integration.

One subtle point is how CoreDNS handles Service records. By default, it generates SRV records for Kubernetes services. If you query for _my-service._tcp.my-namespace.svc.cluster.local, CoreDNS will return an SRV record pointing to the service’s ClusterIP and port. This is crucial for applications that rely on SRV records for service discovery.

The next hurdle you’ll likely face is understanding how to configure custom DNS entries for your Kubernetes cluster or how to integrate external DNS zones.

Want structured learning?

Take the full Coredns course →