What is CoreDNS?

CoreDNS is different from other DNS servers, such as (all excellent)
BIND,
Knot,
PowerDNS and
Unbound (technically a resolver, but still worth a mention), because it
is very flexible, and because it chains plugins.

Plugins can be stand-alone or work together to perform a “DNS function”.

So what’s a “DNS function”? For the purpose of CoreDNS, we define it as a piece of software that
implements the CoreDNS Plugin API. The functionality implemented can wildly deviate. There are
plugins that don’t themselves create a response, such as metrics or
cache, but that add functionality. Then there are plugins that do generate
a response. These can also do anything: There are plugins that communicate with
Kubernetes to provide service discovery, plugins that read data from
a file or a database.

There are currently about 30 plugins included in the default CoreDNS install, but there are also a whole
bunch of external plugins that you can compile into CoreDNS to extend its
functionality.

CoreDNS is powered by plugins.

Writing new plugins is easy enough, but requires knowing Go and having
some insight into how DNS works. CoreDNS abstracts away all other bits, so that you can just
focus on writing the plugin functionality you need.

Modified: 23 Feb, 2018

Installation

CoreDNS is written in Go, but unless you want to develop plugins or compile CoreDNS yourself, you
probably don’t care. The following sections detail how you can get CoreDNS binaries or install from source.

Binaries

For every CoreDNS release, we provide pre-compiled
binaries for various operating systems. For
Linux, we also provide cross-compiled binaries for ARM, PowerPC and other architectures.

Docker

We push every release as Docker images as well. You can find them in the public Docker
hub for the CoreDNS organization.

Note that Docker images that are for architectures other than AMD64 don’t have any certificates
installed. This means if you want to use CoreDNS on ARM and do things like DNS-over-TLS, you’ll need
to create your own Docker image.

Source

To compile CoreDNS, we assume you have a working Go setup. See various tutorials if you don’t have
that already configured. The Go version that comes with your OS is probably too old to compile
CoreDNS as we require Go 1.9.x at the moment (Feb. 2018).

With CoreDNS, we try to vendor as many of our dependencies as possible, but because of various
reasons (mostly making it
possible for external plugins to compile), we can not vendor all our dependencies. Hence to compile
CoreDNS, you still need to go get some packages. The Makefile we include handles all of these
steps. So compiling CoreDNS boils down to (as of this writing the latest version is 1.0.5):

Testing

Once you have a coredns binary, you can use the -plugins flag to list all the compiled plugins.
Without a Corefile (See Configuration) CoreDNS will load the
whoami plugin that will respond with the IP address and port of the client. So to
test, we start CoreDNS to run on port 1053 and send it a query using dig:

Plugins

Once CoreDNS has been started and has parsed the configuration, it runs Servers.
Each Server is defined by the zones it serves and on what port. Each Server has
its own Plugin Chain.

When a query is being processed by CoreDNS, the following steps are performed:

If there are multiple Servers configured that listen on the queried port, it will check which one
has the most specific zone for this query (longest suffix match). E.g. if there are two Servers,
one for example.org and one for a.example.org, and the query is for www.a.example.org, it
will be routed to the latter.

Once a Server has been found, it will be routed through the Plugin Chain that is configured for
this server. This always happens in the same order. That (static) ordering is defined in
plugin.cfg.

Each plugin will inspect the query and determine if it should process it (some plugins
allow you to filter further on the query name or other attributes).
A couple of things can now happen:

The query is processed by this plugin.

The query is not processed by this plugin.

The query is processed by this plugin, but half way through it decides it still wants
to call the next plugin in the chain. We call this fallthrough after the keyword that
enables it.

The query is processed by this plugin, a “hint” is added and the next plugin is called. This
hint provides a way to “see” the (eventual) response and act upon that.

Processing a query means a Plugin will respond to the client with a reply.

Note that a plugin is free to deviate from the above list as it wishes. Currently, all plugins that
come with CoreDNS fall into one of these four groups though. Note this blog
post also provides background in the query
routing.

Query Is Processed

The plugin processes the query. It looks up (or generates, or whatever the plugin author decided
this plugin does) a response and sends it back to the client. The query processing stops here, no
next plugin is called. A (simple) plugin that works like this is whoami.

Query is not processed

If the plugin decides it will not process a query, it simply calls the next plugin in the chain.
If the last plugin in the chain decides to not process the query, CoreDNS will return SERVFAIL back
to the client.

Query is processed With Fallthrough

In this situation, a plugin handles the query, but the reply it got from its backend (i.e. maybe it
got NXDOMAIN) is such that it wants other plugins in the chain to take a look as well. If fallthrough
is provided (and enabled!), the next plugin is called. A plugin that works like this is the
hosts plugin.
First, a lookup in the host table (/etc/hosts) is attempted, if it finds an answer, it returns that.
If not, it will fallthrough to the next one in the hope that other plugins may return something to the
client.

Query is processed with a hint

A plugin of this kind will process a query, and will always call the next plugin. However, it provides
a hint that allows it to see the response that will be written to the client. A plugin that does
this is prometheus. It times the duration …

Unregistered Plugins

There is another, special class of plugins that don’t handle any DNS data at all, but influence how
CoreDNS behaves in other ways. Take for instance the bind plugin that controls to
which interfaces CoreDNS should bind. The following plugins fall into this category:

Anatomy of Plugins

The Setup parses the configuration and the Plugin’s Directives (those should be documented in the
plugin’s README).

The Handler is the code that processes the query and implements all the logic.

The Registration part registers the plugin in CoreDNS - this happens when CoreDNS is compiled. All
of the registered plugins can be used by a Server. The decision of which plugins are configured
in each Server happens at run time and is done in CoreDNS’s configuration file, the Corefile.

Plugin Documenation

Each plugin has its own README detailing how it can be configured. This README includes examples and
other bits a user should be aware of. Each of these READMEs end up on https://coredns.io/plugins,
and we also compile them into manual pages.

Modified: 23 Feb, 2018

Configuration

There are various pieces that can be configured in CoreDNS. The first is determining which
plugins you want to compile into CoreDNS. The binaries we provide have all plugins, as listed in
plugin.cfg, compiled in.
Adding or removing is easy, but requires a recompile of CoreDNS.

Thus most users use the Corefile to configure CoreDNS. When CoreDNS starts, and the -conf flag is
not given, it will look for a file named Corefile in the current directory. That file consists
of one or more Server Blocks. Each Server Block lists one or more Plugins. Those Plugins may be
further configured with Directives.

The ordering of the Plugins in the Corefile does not determine the order of the plugin chain. The
order in which the the plugins are executed is determined by the ordering in plugin.cfg.

Comments in a Corefile are started with a #. The rest of the line is then considered a comment.

Environment Variables

CoreDNS supports environment variables in its configuration.
They can be used anywhere in the Corefile. The syntax is {$ENV_VAR} (a more Windows-like syntax
{%ENV_VAR%} is also supported). CoreDNS substitutes the contents of the variable while parsing
the Corefile.

Importing Other Files

See the import plugin. This plugin is a bit special in that
it may be used anywhere in the Corefile.

Reusable Snippets

A special case of importing files is a snippet. A snippet is defined by naming a block with
a special syntax. The name has to be put in parentheses: (name). After that, it can be included in
other parts of the configuration with the import plugin:

Server Blocks

Each Server Block starts with the zones the Server should be authoritative for. After the zone
name or a list of zone names (separated with spaces), a Server Block is opened with an opening brace.
A Server Block is closed with a closing brace. The following Server Block specifies a server that is
responsible for all zones below the root zone: .; basically, this server should handle every
possible query:

. {
# Plugins defined here.
}

Server blocks can optionally specify a port number to listen on. This defaults to port 53 (the
standard port for DNS). Specifying a port is done by listing the port after the zone separated by
a colon. This Corefile instructs CoreDNS to create a Server that listens on port 1053:

.:1053 {
# Plugins defined here.
}

Note: if you explicitly define a listening port for a Server you can’t overrule it with the
-dns.port option.

Specifying a Server Block with a zone that is already assigned to a server and running it on the
same port is an error. This Corefile will generate an error on startup:

.:1054 {
}
.:1054 {
}

Changing the second port number to 1055 makes these Server Blocks two different Servers.

Specifying a Protocol

Currently CoreDNS accepts three different protocols: plain DNS, DNS over TLS and DNS over gRPC. You
can specify what a server should accept in the server configuration by prefixing a zone name with
a scheme.

dns:// for plain DNS (the default if no scheme is specified).

tls:// for DNS over TLS.

grpc:// for DNS over gRPC.

Plugins

Each Server Block specifies a number of plugins that should be chained for this specific Server. In
its most simple form, you can add a Plugin by just using its name in a Server Block:

. {
chaos
}

The chaos plugin makes CoreDNS answer queries in the CH class - this can be useful for identifying
a server. With the above configuration, CoreDNS will answer with its version when getting a request:

External Plugins

External plugins are plugins that are not compiled into the default CoreDNS. You can easily enable
them, but you’ll need to compile CoreDNS your self.

Possible Errors

The health plugin’s documentation states “This plugin only needs to be enabled once”,
which might lead you to think that this would be a valid Corefile:

health
. {
whoami
}

But this doesn’t work and leads to a somewhat cryptic error:

"Corefile:3 - Error during parsing: Unknown directive '.'".

What happened here? health is seen as a zone (and the start of a Server Block). The parser expects to
see plugin names (cache, etcd, etc.), but instead the next token is ., which isn’t a plugin.
The Corefile should be constructed as follows:

. {
whoami
health
}

That line in the health plugin’s documentation means that once health is specified, it is global for
the entire CoreDNS process, even though you’ve only specified it for one server.

Modified: 23 Feb, 2018

Setups

Here you can find a bunch of configurations for CoreDNS. All setups are done assuming you are not the
root user and hence can’t start listening on port 53. We will use port 1053 instead, using the
-dns.port flag. In every setup, the configuration file used is the CoreDNS’ default, named Corefile.
This means we don’t need to specify the configuration file with the -conf flag. In other words,
we start CoreDNS with ./coredns -dns.port=1053 -conf Corefile, which can be abbreviated to
./coredns -dns.port=1053.

All DNS queries will be generated with the dig
tool, the gold standard for debugging DNS. The full command line we use here is:

$ dig -p 1053 @localhost +noall +answer <name> <type>

But we shorten it in the setups below, so dig www.example.org A is really
dig -p 1053 @localhost +noall +answer www.example.org A

Authoritative Serving From Files

This setup uses the file plugin. Note the external redis plugin
enables authoritative serving from a Redis Database. Let’s continue with the setup using file.

The file we create here is a DNS zone file, and it can have any name (file plugin doesn’t care). The
data we are putting in the file is for the zone example.org..

In your current directory, create a file named db.example.org and put the following contents in
it:

The above logs show us the address CoreDNS replied from (::1) and the time and date it replied.
Furthermore, it logs the query type, the query class, the query name, the protocol used (udp), the
size in bytes of the incoming request, the DO bit state, and the advertised UDP buffer size. This is
data from the incoming query. NOERROR signals the start of the reply, which is the Response Code
sent back, followed by the set of flags on the reply: qr,aa,rd,ra, the size of the reply in bytes
(121), and the duration it took to get the reply.

Forwarding

CoreDNS can be configured to forward traffic to a recursor. We currently have two plugins that allow
for this, proxy and forward. Here, we will use forward
and focus on the most basic setup: forwarding to Google Public DNS (8.8.8.8) and Quad9 DNS
(9.9.9.9).

We don’t need to create anything except for a Corefile with the configuration we want. In
this case, we want all queries hitting CoreDNS to be forward to either 8.8.8.8 or 9.9.9.9:

. {
forward . 8.8.8.8 9.9.9.9
log
}

Note that forward and proxy allow you to fine tune the names it will send upstream. Here, we
chose all names (.). For instance: forward example.com 8.8.8.8 9.9.9.9 would only forward names
within the example.com. domain.

Forwarding Domains To Different Upstreams

A common scenario you may encounter is that queries for example.org need to go to 8.8.8.8 and
the rest should be resolved via the name servers in /etc/resolv.conf. There are two ways that
could be implemented in a Corefile; one way that may work (depending on the plugin’s implementation) and
a way that is guaranteed to work.

Take this Corefile as an example:

. {
forward example.org 8.8.8.8
forward . /etc/resolv.conf
log
}

The intent is to grab all possible queries (this Server Block is authoritative for the root domain),
and then use the per-zone filtering of the forward plugin. Spoiler alert: this
does not work. The reason is that the forward plugin can only be used once in a Server
Block (it used to silently overwrite the previous configuration; now the above config triggers an
error).

The above use case is a very valid one, so how do you implement this in CoreDNS? The quick
answer is by using multiple Server Blocks, one for each of the domains you want to route
on. Doing so results in this Corefile:

This leaves the domain routing to CoreDNS, which also handles special cases like DS queries. Having
two smaller Server Blocks instead of one has no negative effects except that your Corefile will be
slightly longer. Things like snippets and the import will help there.

Kubernetes

Federation

Autopath

Metrics

Caching

Recursive Resolver

CoreDNS does not have a native (i.e. written in Go) recursive resolver, but there is an (external)
plugin that utilizes libunbound. For this setup to work, you first
have to recompile CoreDNS and enable the unbound
plugin. Super quick
primer here (you must have the CoreDNS source installed):

Add unbound:github.com/coredns/unbound to plugin.cfg.

Do a go generate, followed by make.

Note: the unbound plugin needs cgo to be compiled, which also means the coredns binary is now
linked against libunbound and not a static binary anymore.

Assuming this worked, you can then enable unbound with the following Corefile:

. {
unbound
cache
log
}

cache has been included, because the (internal) cache from unbound is disabled to allow the
cache’s metrics to works just like normal.

Modified: 23 Feb, 2018

Writing Plugins

As mentioned before in this manual, plugins are the thing that make CoreDNS tick. We’ve seen
a bunch of configuration in the previous section, but how can you write your own plugin?

The canonical example plugin is the example plugin. Its github
repository shows the most minimal code (with tests!) that is
needed to create a plugin.

It has:

setup.go and setup_test.go, which implement the parsing of configuration from the Corefile.
The (usually named) setup function is called whenever the Corefile parser see the plugin’s
name; in this case, “example”.

example.go (usually named <plugin_name>.go), which contains logic for handling the query, and
example_test.go, which has basic units tests to check if the plugin works.

The README.md that documents in a Unix manual style how this plugin can be configured.

A LICENSE file. For inclusion in CoreDNS, this needs to have an APL like license.

The code also has extensive comments; feel free to fork it and base your plugin off of it.

How Plugins Are Called

When CoreDNS wants to use a plugin it calls the method ServeDNS. ServeDNS has three parameters:

a context.Context;

a dns.ResponseWriter that is basically the client’s connection;

a *dns.Msg that is the request from the client.

ServeDNS returns two values: a (response) code and an error. The error is logged when the
errors is used in this server.

The code tells CoreDNS if a reply has been written by the plugin chain or not. In the latter case,
CoreDNS will take care of that. For the code’s values, we reuse the DNS return codes (rcodes) from
the dns package.

CoreDNS treats:

SERVFAIL (dns.RcodeServerFailure)

REFUSED (dns.RcodeRefused)

FORMERR (dns.RcodeFormatError)

NOTIMP (dns.RcodeNotImplemented)

as special and will then assume nothing has been written to the client. In all other cases, it
assumes something has been written to the client (by the plugin).

Logging From a Plugin

If your plugin needs to output a log line, you should use the log package. CoreDNS does not
implement log levels. The standard way of outputting is: log.Printf("[LEVEL] ..."), and LEVEL
can be: INFO, WARNING or ERROR.

In general, logging should be left to the higher layers when returning an error. However, if there is
a reason to consume the error but still notify the user, then logging in the plugin can be acceptable.

Metrics

When exporting metrics, the Namespace should be plugin.Namespace (=“coredns”), and the
Subsystem should be the name of the plugin. The README.md for the plugin should then also contain
a Metrics section detailing the metrics. If the plugin supports dynamic health
reporting, it should also have a Health section detailing some of its inner workings.

Documentation

Each plugin should have a README.md explaining what the plugin does and how it is configured. The
file should have the following layout:

Example Domain Names

Please be sure to use example.org or example.net in any examples and tests you provide. These
are the standard domain names created for this purpose. If you don’t, there is a chance your fantasy
domain name is registered by someone and will actually serve web content (which you may like or not).

Fallthrough

In a perfect world, the following would be true for plugins: “Either you are responsible for a zone or
not”. If the answer is “not”, the plugin should call the next plugin in the chain. If “yes” it
should handle all names that fall in this zone and the names below - i.e. it should handle the
entire domain and all sub domains.

TODO(miek): ref to “Query Is Proccessed with Fallthrough”

. {
file example.org db.example
}

In this example the file plugin is handling all names below (and including) example.org. If
a query comes in that is not a subdomain (or equal to) example.org the next plugin is called.

Now, the world isn’t perfect, and there are good reasons to “fallthrough” to the next middleware,
meaning a plugin is only responsible for a subset of names within the zone. The first of these
to appear was the reverse plugin that synthesizes PTR and A/AAAA responses (useful with IPv6).

The nature of the reverse plugin is such that it only deals with A/AAAA and PTR, and then only
for a subset of the names. Ideally, you would want to layer reversein front of another
plugin such as file or auto (or even proxy). This means reverse handles some special
reverse cases and all other requests are handled by the backing plugin. This is exactly what
“fallthrough” does. To keep things explicit we’ve opted that plugins implementing such behavior
should implement a fallthrough keyword.

The fallthrough directive should optionally accept a list of zones. Only queries for records
in one of those zones should be allowed to fallthrough.

Qualifying for main repo

Plugins for CoreDNS can live out-of-tree. plugin.cfg defaults to CoreDNS’s repo, but external
repos work fine. So when do we consider the inclusion of a new plugin in the main repo?

The plugin authors should be willing to maintain the plugin, i.e. your GitHub handle will be
listed in its OWNERS file.

The plugin should be useful for other people. “Useful” is a subjective term, but it should
bring something new to CoreDNS.

It should be sufficiently different from other plugins to warrant inclusion.

Current Internet standards need be supported: IPv4 and IPv6, so A and AAAA records should be
handled (if your plugin is in the business of dealing with address records, that is).