CoreDNS and Unbound are both excellent recursive DNS resolvers, but they solve slightly different problems and have different architectural philosophies.

Let’s see CoreDNS in action handling a simple DNS query. Imagine you have a Corefile like this:

.:53 {
    forward . 8.8.8.8 1.1.1.1
    cache 30
    reload
    log
}

When your client asks for www.example.com, CoreDNS, running on port 53, first checks its cache for 30 seconds. If it’s not there, it forwards the query to Google’s 8.8.8.8 and Cloudflare’s 1.1.1.1. Once it gets an answer, it caches it for 30 seconds and then returns it to the client. The reload plugin allows you to update the Corefile without restarting the server, and log shows you the queries it’s processing.

Now, consider Unbound. A typical Unbound configuration might look like this:

server:
    interface: 0.0.0.0@53
    access-control: 192.168.1.0/24 allow
    root-hints: "/etc/unbound/root.hints"
    cache-min-ttl: 60
    cache-max-ttl: 86400

remote-control:
    control-enable: yes

If a client from 192.168.1.0/24 asks Unbound for www.example.com, Unbound doesn’t forward to specific public resolvers by default. Instead, it will query the root DNS servers directly using the root.hints file. It caches responses with a minimum TTL of 60 seconds and a maximum of 24 hours. The remote-control section allows you to manage Unbound’s configuration and view statistics remotely.

The fundamental problem CoreDNS aims to solve is providing a flexible, plugin-driven DNS server, especially within cloud-native environments like Kubernetes. Its extensibility means you can add features like load balancing, service discovery integration, or even custom authentication directly into the DNS resolution process. Unbound, on the other hand, is built from the ground up as a highly secure, validating, and high-performance recursive resolver. Its primary focus is on correctness, speed, and privacy by performing full DNSSEC validation and minimizing trust in upstream servers.

Internally, CoreDNS is written in Go and uses a middleware-style architecture. Each plugin in the Corefile is a piece of middleware that can process, modify, or forward a DNS request. This makes it incredibly adaptable; you can easily reorder or add plugins to change how DNS is handled. Unbound is written in C and is designed as a monolithic, highly optimized resolver. It has a robust internal state machine for handling the recursive process, including iterative queries, caching, and validation.

The exact levers you control differ. With CoreDNS, you’re primarily manipulating the Corefile to enable or disable plugins and configure their behavior. You can specify different upstream forwarders, adjust cache sizes and TTLs, implement access control, or even add custom DNS record types. With Unbound, you’re configuring its unbound.conf file. This involves setting network interfaces, access control lists, specifying root hints, tuning cache parameters, enabling or disabling DNSSEC validation, and configuring performance-related options like thread counts and buffer sizes.

Most people understand that CoreDNS is good for Kubernetes and Unbound is good for performance and security. What’s often overlooked is how CoreDNS’s forward plugin can be configured to perform DNSSEC validation if the upstream forwarders support it, effectively bridging some of Unbound’s core security features into a more flexible, plugin-based system. It’s not an all-or-nothing security proposition for CoreDNS.

The next natural step is to explore how to configure robust DNSSEC validation in CoreDNS.

Want structured learning?

Take the full Coredns course →