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
Router (recommended)
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
Corefileis a directory instead of a file- CRLF line endings causing parse errors
Modifying the Corefile
- Edit
infra/coredns/Corefile - 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).