Setting Up Local Domain Name Service with CoreDNS

Introduction⌗
After getting used to the convenience of laravel/valet, in development environments built with Docker, we often need to set up local domain names for projects in the local /etc/hosts
file. This leads to a problem where each person might configure different domain names, and team members often need to communicate and reproduce issues through URLs.
For example, if developer A gives the CRM project the domain name crm.localhost
, and developer B gives it crm.dev
, this results in needing to define a set of environments for each person in our shared API debugger. At the same time, when reproducing issues, it also increases communication costs.
To maintain environment consistency as much as possible, we will embed service access domain names in Docker Compose orchestration through configuration files. This way, everyone just needs to execute docker compose up -d
to get a consistent development environment, including domain names and HTTPS certificates!
Selection⌗
To achieve a highly unified vision, I researched the following open-source projects:
- dnsmasq
- CoreDNS
- AdGuardDNS
Since laravel/valet also uses dnsmasq, I initially considered choosing dnsmasq, but after searching around, I couldn’t find an officially maintained Docker Image. There are some versions maintained by individual developers, but I decided against using them.
Later, I discovered that CoreDNS is developed in Go language and is a graduated project incubated by Cloud Native, so the future community support is definitely assured. After a simple trial, I found it basically meets our needs and supports wildcard resolution.
AdGuard Home was an unexpected find. I say this because it’s aimed at ordinary users rather than developers, providing a clean and beautiful management interface. However, it’s not quite suitable for us because it’s mainly used to block ads, and configuration requires logging into the admin backend to configure custom domain rewrites, though it also supports wildcard resolution.
After comparison, we finally chose CoreDNS, partly due to its stronger community, rich plugins, and ability to ensure consistency through configuration files.
Getting the Image⌗
Because our team uses M1 Chips based on ARM architecture, and the official currently doesn’t provide a Docker Image for ARM architecture, we built our own ARM architecture Docker Image.
docker pull betterde/coredns
Other CPU architectures can pull the official CoreDNS Docker Image
Configuration⌗
First, create a docker-compose.yml
container orchestration configuration file:
services:
coredns:
image: betterde/coredns:latest
ports:
- 53:53/udp
restart: always
command: -conf /root/Corefile
volumes:
- ./config:/root
hostname: coredns
container_name: coredns
The new version of Docker Compose no longer requires defining
version
.
Next, create the CoreDNS configuration folder:
mkdir config
For example, if we use betterde.it
as the TLD for our local development environment, create a Corefile configuration file in the folder:
.:53 {
forward . 223.5.5.5 223.6.6.6
log
errors
}
betterde.it:53 {
file /root/betterde.it.db
log
loop
reload
errors
}
Finally, create a data file betterde.it.db
for defining domain resolution:
betterde.it. IN SOA dns.betterde.it. george.example.com. 2017042745 7200 3600 1209600 3600
*.betterde.it. IN A 127.0.0.1
dns.betterde.it. IN A 127.0.0.1
host.betterde.it. IN A 192.168.1.10
server.betterde.it. IN CNAME host.betterde.it.
If you’ve configured BIND domain server zones before, the
betterde.it.db
configuration file should be familiar. Thegeorge.example.com.
is the administrator’s email, and since@
has other uses inzone
configuration files,.
is used as a replacement here.
Starting the Service⌗
docker compose up -d
Verifying Results⌗
dig @127.0.0.1 dns.betterde.it
; <<>> DiG 9.10.6 <<>> @127.0.0.1 dns.betterde.it
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37167
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;dns.betterde.it. IN A
;; ANSWER SECTION:
dns.betterde.it. 0 IN A 127.0.0.1
;; Query time: 21 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Nov 28 15:49:28 CST 2022
;; MSG SIZE rcvd: 67
Using CoreDNS⌗
On macOS, you can configure your own DNS through the following steps and management commands:
- Check system DNS priority
scutil --dns
DNS configuration
resolver #1
nameserver[0] : 223.6.6.6
nameserver[1] : 202.46.34.75
if_index : 14 (en0)
flags : Request A records
reach : 0x00000002 (Reachable)
resolver #2
domain : local
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300000
resolver #3
domain : 254.169.in-addr.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300200
resolver #4
domain : 8.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300400
resolver #5
domain : 9.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300600
resolver #6
domain : a.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300800
resolver #7
domain : b.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 301000
resolver #8
domain : it
nameserver[0] : 127.0.0.1
flags : Request A records, Request AAAA records
reach : 0x00030002 (Reachable,Local Address,Directly Reachable Address)
DNS configuration (for scoped queries)
resolver #1
nameserver[0] : 223.6.6.6
nameserver[1] : 202.46.34.75
if_index : 14 (en0)
flags : Scoped, Request A records
reach : 0x00000002 (Reachable)
We can see that it’s currently using Alibaba Cloud DNS 223.6.6.6 and Telecom 202.46.34.75 as DNS.
- List all network connection methods:
networksetup -listallnetworkservices
An asterisk (*) denotes that a network service is disabled.
USB-LAN
Wi-Fi
Thunderbolt Bridge
iPhone USB
GAVPN
Tailscale Tunnel
- Set DNS server for a specific network connection method
networksetup -setdnsservers Wi-Fi 127.0.0.1
For example, I’m connected via Wi-Fi, so I’m setting the DNS query for the Wi-Fi device to go through the local Loopback address
127.0.0.1
.
- Verify results
scutil --dns
DNS configuration
resolver #1
nameserver[0] : 127.0.0.1
flags : Request A records, Request AAAA records
reach : 0x00030002 (Reachable,Local Address,Directly Reachable Address)
resolver #2
domain : local
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300000
resolver #3
domain : 254.169.in-addr.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300200
resolver #4
domain : 8.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300400
resolver #5
domain : 9.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300600
resolver #6
domain : a.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 300800
resolver #7
domain : b.e.f.ip6.arpa
options : mdns
timeout : 5
flags : Request A records
reach : 0x00000000 (Not Reachable)
order : 301000
resolver #8
domain : it
nameserver[0] : 127.0.0.1
flags : Request A records, Request AAAA records
reach : 0x00030002 (Reachable,Local Address,Directly Reachable Address)
DNS configuration (for scoped queries)
resolver #1
nameserver[0] : 127.0.0.1
if_index : 14 (en0)
flags : Scoped, Request A records, Request AAAA records
reach : 0x00000000 (Not Reachable)
From the above results, we can see that it’s now using 127.0.0.1 as DNS.
Conclusion⌗
dig www.baidu.com
; <<>> DiG 9.10.6 <<>> www.baidu.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40287
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.baidu.com. IN A
;; ANSWER SECTION:
www.baidu.com. 196 IN CNAME www.a.shifen.com.
www.a.shifen.com. 196 IN A 180.101.49.13
www.a.shifen.com. 196 IN A 180.101.49.14
;; Query time: 22 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Nov 28 16:02:29 CST 2022
;; MSG SIZE rcvd: 149
dig crm.betterde.it
; <<>> DiG 9.10.6 <<>> crm.crm.betterde.it
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28646
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;crm.crm.betterde.it. IN A
;; ANSWER SECTION:
crm.crm.betterde.it. 0 IN A 127.0.0.1
;; Query time: 5 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Nov 28 16:04:42 CST 2022
;; MSG SIZE rcvd: 67
With this, a small but complete DNS setup is finished, and it will become a necessary infrastructure for our team collaboration!
If you need to stop using CoreDNS, you can use the following commands:
# Clear the device's current DNS settings
networksetup -setdnsservers Wi-Fi empty
# Check the device's current DNS settings
networksetup -getdnsservers Wi-Fi
Finally, clear the DNS cache
dscacheutil -flushcache
I hope this is helpful, Happy hacking…