By: Josiah Huckins - 7/24/2025
minute read
If you host (or plan to) any services from home or a small branch office, you can configure friendly domains under a local zone.
Instead of accessing your NAS from some IP address, you could use mynas.internal. Instead of opening your home lab's console using 192.168.1.12 (hypothetical local address), you could use homelab.myhome.This is possible with your own DNS server! Improve ease of use, integrity and allow for more options in access control. In this quick post, I'll walk through setting one up.
DNS Server Options
There are actually many options for this, including dynamic DNS services like NoIP. If you have access to Windows Server, you can configure resolvers ready to handle heavy query volumes.Your router very likely has its own built in DNS and/or DDNS server that you could use. This is what I was using for some time, but with a router hardware upgrade, the firmware I normally flash (to add dnsmasq support) is not yet available. I started looking for alternatives.
A lightweight, self-hostable option is CoreDNS. CoreDNS is written in Go, it's fast and best of all, free!
Setting up
You'll need docker installed on the system where this is being set up.First, create a dedicated folder called dns-service. Then in that folder, create a file named Dockerfile with the following contents.
This will create an Alpine Linux container, build CoreDNS and install it. This also copies the Corefile and zone folders into the container, these are used to configure CoreDNS (more on this below).
Next, in the dns-service folder create a docker-compose.yml file with the following contents.
This uses the Dockerfile to build the container, maps port 53 on the host to container port 53 for TCP and UDP protocols and exposes this port. (Port 53 is the standard service port for DNS.) Use of docker compose is a best practice, you could expose the port in the Dockerfile and execute docker build and run commands, but compose makes it easy to add other services later on.
Alright, now just three remaining files are needed to complete this. First among them, in dns-service create a file named Corefile (no extension) with the following contents.
This file defines the forwarding configuration. If DNS queries cannot be resolved by this server they will be forwarded to the IP addresses defined here. Notice I'm using Google's DNS servers, but you can use whichever public servers you want, even another local DNS server. Multiple addresses are supported, space separated.
This file also defines the location of the internal zone database. We'll set that up next.
Create a folder in the dns-service folder named zones.
In the zones folder, create a file named internal.db with the following contents.
This file is where you create your domain to IP address A records. In the sample file, I've created records for 3 domains: service1.internal, vscode.internal and immich.internal. Following the pattern of these, you can add any domains you need.
Side note, it's strongly advised to enable static IPs or DHCP address reservations for your service hosts. That way the A records in this configuration are always valid and rarely if ever need to be changed. Your router usually has options for this.
Security Considerations
Okay, the last thing we need to do is ensure DNSSEC is enabled. This is important to authenticate the responses of DNS queries. Clients making DNS requests use this behind the scenes, to make sure the response from the DNS server/resolver has not been modified by an attacker in transit.Note, this isn't what guarantees privacy protection for lookups. Privacy here would require encryption via DNS over TLS (which we won't get into here).
In the Dockerfile, you may have noticed we copy a plugin configuration into the CoreDNS build directory. In the dns-server folder, create a file named plugin.cfg with the following contents.
This file enables the DNSSEC plugin. After all of the above, navigate to the dns-server directory in a terminal and run docker compose up -d. This will build and start your new DNS service!