The CoreDNS hosts plugin lets you inject custom DNS records into your cluster’s DNS resolution, effectively bypassing upstream DNS servers for specific domains.

Imagine you have a service running in your cluster that needs to talk to an external API. The API’s DNS record might be slow to update, or you might want to point it to a specific IP address for testing or failover. The hosts plugin is your direct line to override that.

Here’s a typical CoreDNS configuration snippet, showing where the hosts plugin fits in:

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .: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
        cache 30
        hosts {
           # Custom records go here
           192.168.1.100 my-external-api.example.com
           10.0.0.5 my-internal-service.local
        }
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }

In this example, 192.168.1.100 my-external-api.example.com tells CoreDNS that any query for my-external-api.example.com should immediately resolve to 192.168.1.100. Similarly, 10.0.0.5 my-internal-service.local overrides the resolution for that internal service. The hosts plugin is placed after the kubernetes plugin. This order is crucial: it means CoreDNS will first try to resolve names using the Kubernetes internal DNS (for pods, services, etc.) and then check its custom hosts file. If a match is found in hosts, that result is returned, and the forward directive (which sends queries to upstream DNS) is never reached for that specific domain.

This provides a powerful mechanism for controlling network access within your cluster without modifying application code or external DNS infrastructure. You can use it to:

  • Point to specific IPs: Direct traffic to a known IP address for a service, bypassing dynamic DNS.
  • Test new deployments: Route traffic for a production domain to a staging or development instance running in your cluster.
  • Simulate network conditions: Force resolution to a specific IP that might be experiencing latency or issues.
  • Override external services: Control how your cluster resolves certain external domain names, perhaps for security policy enforcement or to direct traffic to a local mirror.

The hosts plugin reads its configuration directly from the Corefile within the CoreDNS ConfigMap. Each line follows the format IP_ADDRESS DOMAIN_NAME [DOMAIN_NAME...]. You can list multiple domain names that should resolve to the same IP address on a single line.

When a DNS query arrives at CoreDNS, it processes the plugins in order. If the hosts plugin is encountered and the requested domain name matches an entry in its list, CoreDNS immediately returns the associated IP address. This happens before the query is forwarded to any external DNS servers. If there’s no match in the hosts plugin, the query continues down the plugin chain, eventually being forwarded to upstream resolvers if no other plugin handles it.

The hosts plugin is particularly useful for scenarios where you need fine-grained control over DNS resolution for a limited set of domains. For instance, if you’re running a Kubernetes cluster and need to ensure that a specific internal application always resolves to a particular IP address, even if that IP changes on the external network, you can hardcode it in the hosts plugin. This avoids the need for complex DNS record updates or application-level configuration changes.

The order of plugins in your Corefile is paramount. If you place the hosts plugin after the forward plugin, your custom records will only be consulted if the upstream DNS server fails to resolve the name, which defeats the purpose of overriding. The typical and most effective placement is before forward and after kubernetes, allowing internal cluster DNS to take precedence, followed by your custom overrides, and finally, upstream DNS resolution.

When you add or modify records in the hosts plugin, CoreDNS needs to reload its configuration. The reload plugin, usually present in a standard CoreDNS setup, watches for changes to the Corefile and automatically reloads the configuration without restarting the CoreDNS pods. This means your DNS overrides can be updated dynamically.

You can verify that your custom records are being served by using dig or nslookup from within a pod in your cluster. For example, if you added 1.2.3.4 mytest.local to your hosts file, you would run:

kubectl exec <your-pod-name> -- dig mytest.local

The output should show 1.2.3.4 as the answer, and the "SERVER" in the dig output should be your CoreDNS service IP.

One subtlety often overlooked is how CoreDNS handles multiple IP addresses for a single hostname within the hosts plugin. While the standard hosts file format on Linux supports multiple IPs per line, CoreDNS’s hosts plugin expects a single IP address per entry. If you need to map a domain to multiple IPs, you must create separate lines for each IP address, all pointing to the same domain name:

1.2.3.4 my-service.example.com
5.6.7.8 my-service.example.com

This ensures that CoreDNS correctly interprets and serves all specified IP addresses for the given hostname.

The next logical step after mastering DNS overrides is understanding how to control the TTL (Time To Live) for these custom records, which is handled by different plugins or by manipulating the hosts format itself if supported by a specific variant.

Want structured learning?

Take the full Coredns course →