TL;DR

Three real-life stories of DNS automation and CoreDNS hacks:

  • Managing DNS zones via Git and CI/CD.
  • Your own nip.io — a service that auto-resolves IPs directly from the name.
  • How to write your own CoreDNS plugin and integrate with Kubernetes.

Story 1. Managing DNS Zones with Git

The Challenge

  • Multiple teams, each needing DNS records for test/staging environments.
  • Changes must be safe (no “oops, everything’s broken” scenarios).
  • Administration should be simple, automated, and rollback-friendly.

The Solution

Using DNSControl

  • A handy tool for describing DNS zones as code.
  • Format is close to RFC 1035.

Sample configuration:

require("vars.js");

// Domains:
D('dso.domain.corp', REG_NONE, DnsProvider(BIND),
    A('@', '10.1.2.3'),
    A('ns1','10.1.2.4'),
    A('ns2','10.1.2.5')
);

// subzones
require("./subzones/poc.js");

// reverse zone
require("./revzones/rev-10-2-2.js");

CI/CD pipeline for automation:

test_job:
  stage: test
  script:
    - dnscontrol check

build_job:
  stage: build
  before_script:
    - cp /etc/coredns/origins/* zones/
  script:
    - dnscontrol push
  after_script:
    - cp zones/*.zone /etc/coredns/origins/
  • Validates config (dnscontrol check) before deployment.
  • Any change can be rolled back via Git.
  • Only certain admins have access to DNS management.

Result

  • Corporate users always have up-to-date DNS.
  • Risks are minimized.
  • Rollbacks are trivial.
  • You can take the config and pipeline for your own use—see the author’s repo.

Story 2. Your Own nip.io: Auto-DNS for IP Addresses

The Problem

  • For pilot deployments, getting DNS records approved takes too long.

  • Need to resolve IPs from names (like nip.io), but:

    • Must work in isolated environments (no outside access).
    • Don’t want to depend on third-party services.

The Solution

Build your own nip.io

  • Use CoreDNS with the template plugin to parse IPs from FQDN.

Example:

dig 2025.devopsconf.ip-20-25-4-7.shturval.link @8.8.8.8

;; ANSWER SECTION:
2025.devopsconf.ip-20-25-4-7.shturval.link. 3600 IN A 20.25.4.7

CoreDNS config:

shturval.link:53 {
    ready
    template IN A shturval.link {
        match ^\S+[.]ip-(?P<a>[0-9]*)-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]shturval[.]link[.]$
        answer "{{ .Name }} 3600 IN A {{ .Group.a }}.{{ .Group.b }}.{{ .Group.c }}.{{ .Group.d }}"
        fallthrough
    }
    rewrite stop type AAAA A
    rewrite stop type MX A
    rewrite stop type HTTPS A
    file /etc/coredns/shturval.link.zone
    cache 3600
    reload
    prometheus localhost:9253
}
  • Deploy locally—even in air-gapped environments.
  • Simple template parsing of IPs from the domain name.
  • Everything is powered by CoreDNS standard plugins.

Story 3. How to Write Your Own CoreDNS Plugin

The Backstory

  • Platform upgrade caused node IPs to change.
  • HAProxy was balancing traffic by static IPs → ingress stopped working.
  • Needed autodiscovery without access to load balancer configs.

The Solution

  • No need for Consul—Kubernetes + CoreDNS + etcd can do it!
  • Write a CoreDNS plugin that watches pods and returns up-to-date IPs.

How it works:

  • CoreDNS tracks pods with a specific annotation.
  • On request—returns all relevant IPs.
  • HAProxy (or any LB) queries CoreDNS via a resolver and always gets fresh IPs.

HAProxy config example:

resolvers shturval-sht-capov
  nameserver ns1 api.shturval-sht-capov.domain.corp:1053
  accepted_payload_size 8192

backend ingress-https-shturval-sht-capov
  balance source
  mode tcp
  server-template ingress 3 default-nginx.ingress.shturval-sht-capov.coreha.shturval:30443 check resolvers shturval-sht-capov init-addr none

Plugin skeleton (Go):

func init() {
    plugin.Register(pluginName, setup)
}

func setup(c *caddy.Controller) error {
    k, err := parse(c)
    if err != nil { /* ... */ }
    k.setWatch(context.Background())
    c.OnStartup(startWatch(k, dnsserver.GetConfig(c)))
    c.OnShutdown(stopWatch(k))
    dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
        k.Next = next
        return k
    })
    return nil
}

func (k *KubeHostport) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
    // ... Build response, return current IPs ...
    writeResponse(w, r, records, nil, nil, dns.RcodeSuccess)
    return dns.RcodeSuccess, nil
}
  • Code explanation:
    • Register and initialize the plugin via CoreDNS API.
    • Pod watcher—just like kubectl get --watch.
    • Caches the list of IPs; hits kube-API only on changes.
    • You can literally write such a plugin in one evening—especially with LLM help 😄

Reflections & Takeaways

  • CoreDNS is a powerful DevOps tool: flexible, extensible, minimal dependencies.
  • 50+ in-tree plugins are officially supported.
  • Making your own plugins and custom builds is truly simple.
  • Code and examples are available in the author’s GitHub. Pull requests welcome!

💡 Key Takeaways

  • Treat your infrastructure as code (IaC)—even DNS!
  • Don’t be afraid to write your own plugins and automate your routine.
  • CoreDNS is a fantastic tool for tricky DNS challenges.

That’s all.