NubleStation

Role

CoreDNS runs in a container and listens on port 53 (UDP and TCP). It is the authoritative DNS server for *.{org}.local on your LAN.

For every DNS query matching *.clinic.local, CoreDNS responds with the NubleStation host’s LAN IP. It doesn’t matter what the subdomain is — all *.clinic.local names resolve to the same IP. Caddy then decides what to serve based on the Host header.

For all other DNS queries (google.com, system lookups), CoreDNS forwards upstream to 8.8.8.8 and 1.1.1.1. Devices configured to use CoreDNS as their DNS server can still reach the internet normally.

Corefile

Generated by the installer:

clinic.local:53 {
    template IN A clinic.local {
        answer "{{ .Name }} 60 IN A 192.168.1.100"
    }
    log
    errors
}

.:53 {
    forward . 8.8.8.8 1.1.1.1
    log
    errors
}

The first block uses CoreDNS’s template plugin to respond to any A record query under clinic.local with the host’s IP. The TTL is 60 seconds — devices cache the answer for 1 minute before re-querying.

The second block is the catch-all forwarding rule. Any query that doesn’t match clinic.local is forwarded to Google and Cloudflare resolvers.

Verifying it works

From the host machine:

# Should print the host's LAN IP
dig @127.0.0.1 console.clinic.local +short
dig @127.0.0.1 tasks.clinic.local +short
dig @127.0.0.1 anything.clinic.local +short

# Internet forwarding should work too
dig @127.0.0.1 google.com +short

From another device on the LAN (after setting DNS to the host’s IP):

nslookup console.clinic.local

Configuring devices to use CoreDNS

Set the primary DNS server in your router’s DHCP settings to the NubleStation host’s IP. All devices that renew their lease will automatically use CoreDNS.

Per device

Set the DNS server to the host’s IP in the device’s network settings. This is the fallback if router-level configuration isn’t possible.

Troubleshooting

See the Troubleshooting guide for common CoreDNS issues:

  • Container restart loop with “zone is not a valid domain name” error
  • Corefile is a directory instead of a file
  • CRLF line endings causing parse errors

Modifying the Corefile

  1. Edit infra/coredns/Corefile
  2. Restart CoreDNS: docker compose -f infra/docker-compose.yml restart coredns

CoreDNS reloads its configuration on restart. There is no hot-reload — the restart is instant (< 1 second) since CoreDNS is stateless.

DNS TTL and caching

The template plugin uses a 60-second TTL. If you change the host’s IP and update the Corefile, devices may continue using the old IP for up to 60 seconds. To flush the cache immediately, restart the DNS client on each device (or set TTL 0 temporarily during migration).