CoreDNS can be configured to act as a recursive DNS resolver, forwarding queries for specific domains to different DNS servers, effectively creating custom name resolution policies.

Let’s see this in action. Imagine you have a private network where internal.example.com resolves only through your internal DNS server at 10.0.0.53. You want your CoreDNS instance, running on 10.0.0.10, to handle all other queries but to send internal.example.com traffic to that internal server.

Here’s how you’d configure it in your CoreDNS Corefile:

.:53 {
    errors
    health {
        lameduck 5s
    }
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
       pods insecure
       fallthrough in-addr.arpa ip6.arpa
    }
    prometheus :9153
    forward . 8.8.8.8 8.8.4.4 {
       max_concurrent 1000
    }
    cache 30
    loop
    reload
    loadbalance
}

internal.example.com:53 {
    errors
    forward . 10.0.0.53
}

In this Corefile:

  • The first block (.:53) is the primary CoreDNS configuration.
    • errors and health are standard plugins for logging and monitoring.
    • ready signals readiness to the orchestrator (like Kubernetes).
    • kubernetes handles DNS resolution for Kubernetes internal services. fallthrough ensures that if a query isn’t found within the Kubernetes cluster, it’s passed to the next plugin.
    • prometheus exposes metrics on port 9153.
    • forward . 8.8.8.8 8.8.4.4 is the key for general internet resolution. It tells CoreDNS to forward any query it can’t resolve locally (using the kubernetes plugin, for instance) to Google’s public DNS servers (8.8.8.8 and 8.8.4.4). The max_concurrent 1000 limits how many queries can be in flight simultaneously to these upstream servers.
    • cache 30 caches DNS responses for 30 seconds.
    • loop detects and prevents DNS loops.
    • reload allows the Corefile to be reloaded without restarting CoreDNS.
    • loadbalance distributes queries across multiple upstream servers if multiple are listed in a forward directive.
  • The second block (internal.example.com:53) is the stub domain configuration.
    • This block specifically targets queries for internal.example.com.
    • forward . 10.0.0.53 directs all queries within the internal.example.com zone exclusively to the DNS server at 10.0.0.53.

When a client on your network queries myhost.internal.example.com, CoreDNS first checks if there’s a specific zone configuration for internal.example.com. It finds the second block and forwards the query to 10.0.0.53. If the query was for google.com, it wouldn’t match the internal.example.com zone, so CoreDNS would fall through to the first block, hit the forward . 8.8.8.8 8.8.4.4 directive, and send the query to Google’s DNS servers.

This setup allows granular control over DNS resolution. You can define multiple stub domains, each pointing to different internal DNS servers, for various private zones.

The stub plugin is a more modern and often preferred way to achieve this for simpler cases, as it consolidates stub domain configurations within the main block. For example, the internal.example.com block above could be replaced by adding stub to the first block:

.:53 {
    errors
    health {
        lameduck 5s
    }
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
       pods insecure
       fallthrough in-addr.arpa ip6.arpa
    }
    prometheus :9153
    forward . 8.8.8.8 8.8.4.4 {
       max_concurrent 1000
    }
    cache 30
    loop
    reload
    loadbalance
    stub {
        # Forward queries for internal.example.com to 10.0.0.53
        10.0.0.53:53
    }
}

However, the separate zone configuration approach offers more flexibility when you need to define distinct Corefile directives (like different cache TTLs or specific logging for a particular domain) that go beyond simple forwarding.

Understanding how CoreDNS matches query names to zone blocks in the Corefile is crucial. It processes the blocks from most specific to least specific. A block with a specific domain name like internal.example.com:53 will always be evaluated before a generic block like .:53. This specificity is how the stub domain configuration reliably intercepts and redirects queries.

The next challenge is often handling DNSSEC validation for your stub domains.

Want structured learning?

Take the full Coredns course →