CoreDNS isn’t actually a DNS server; it’s a plugin-based server that can act as a DNS server by loading the right plugins.

Let’s see it in action. Imagine a simple Corefile that just serves DNS records from a static file and forwards everything else to Google’s DNS:

.:53 {
    file /etc/coredns/db.example.com
    forward . 8.8.8.8
}

www.example.com:53 {
    file /etc/coredns/db.www.example.com
}

When a client asks for www.example.com, CoreDNS loads the file plugin for that zone. If it doesn’t find the record in db.www.example.com, it might then fall through to the default .:53 block, and if that also doesn’t have it, the forward plugin would kick in. The beauty is you can chain these behaviors – logging, caching, DNSSEC validation, health checks, and more – in any order you want, for any specific domain or port. It’s a DNS toolkit, not a fixed server.

To understand this better, let’s trace a query. We’ll enable debug logging in CoreDNS to see exactly what’s happening under the hood. This is crucial for debugging why a DNS resolution might be failing or taking too long.

First, locate your Corefile. This is typically in /etc/coredns/Corefile on Linux systems. You’ll need to edit this file to increase the logging verbosity.

The key is to add the log plugin and set its verbosity level. For detailed tracing, you want a high verbosity. A common choice is 5.

Here’s how you’d modify the Corefile to enable debug logging for all zones:

.:53 {
    log . {
        level debug
        format json
    }
    errors
    health {
       lameduck 5s
    }
    ready
    file /etc/coredns/db.example.com
    forward . 8.8.8.8
}

In this snippet:

  • log .: This applies the logging plugin to the root zone (.).
  • { ... }: This block contains the configuration for the log plugin.
  • level debug: This is the critical part. It tells the log plugin to output messages with debug severity and higher. Other levels include info, warn, error, and fatal.
  • format json: This is optional but highly recommended for structured logging, making it easier to parse and analyze the output with tools like jq.

After modifying your Corefile, you need to reload or restart your CoreDNS service for the changes to take effect. The command for this varies depending on your system’s init system:

For systemd:

sudo systemctl reload coredns

or if reload doesn’t work (some older versions might require a restart):

sudo systemctl restart coredns

For SysVinit or Upstart:

sudo service coredns reload

or

sudo service coredns restart

Once CoreDNS has reloaded, you can view the debug logs. The output typically goes to standard output if you’re running CoreDNS manually, or to your system’s journal if it’s managed by systemd.

To view logs with systemd:

sudo journalctl -u coredns -f -n 100
  • -u coredns: Filters logs for the coredns unit.
  • -f: Follows the log output in real-time.
  • -n 100: Shows the last 100 lines initially.

Now, perform a DNS query from a client that uses this CoreDNS server. For example, using dig:

dig www.example.com @<coredns_ip_address>

In your journal output, you’ll start seeing detailed messages. You’ll see the incoming query, how CoreDNS processes it through the configured plugins, and the response generated. If you used format json, you can pipe the output to jq for easier inspection:

sudo journalctl -u coredns -n 1000 | jq .

This will show you JSON objects for each log entry, including timestamps, the plugin that generated the log, the message content, and potentially details about the query and response. For instance, you might see entries like:

{
  "ts": "2023-10-27T10:30:00.123456789Z",
  "level": "debug",
  "logger": "log",
  "msg": "request",
  "request": {
    "remote": "192.168.1.10:54321",
    "proto": "udp",
    "qname": "www.example.com.",
    "qtype": "A",
    "remote_port": 54321,
    "server_port": 53
  }
}
{
  "ts": "2023-10-27T10:30:00.123456789Z",
  "level": "debug",
  "logger": "file",
  "msg": "serving record",
  "zone": "www.example.com.",
  "record": "A 192.0.2.1"
}

This level of detail allows you to pinpoint exactly which plugin handled the request, what data it found or didn’t find, and why a particular response was sent.

The most surprising thing about CoreDNS’s plugin architecture is that the order of plugins in the Corefile is not just about precedence; it’s about creating a pipeline where each plugin can mutate or even replace the request/response entirely before passing it to the next.

When you’re done debugging, remember to revert the Corefile back to its original state or a less verbose logging level (e.g., level warn) to avoid excessive log generation and disk I/O. Reload CoreDNS again after making these changes.

The next hurdle you’ll likely face is correlating these detailed CoreDNS logs with the actual client-side DNS resolution behavior.

Want structured learning?

Take the full Coredns course →