Programster's Blog

Tutorials focusing on Linux, programming, and open-source

Deploy Your Own DNS Server With Docker

DNS

This is a quick tutorial on how to deploy your own DNS service easily through Docker. Having a DNS server is an extremely good idea if you are a Linux enthusiast or a developer. It's much easier to remember names than IP's and having one location that maps all the IPs to hostnames is extremely useful. Because it is deployed through docker, you can use the Linux OS of your choice.

Steps

If you haven't already, install docker on the host server

Create a hosts file within a directory path of: volumes/config/hosts in your home folder.

Create a file in that folder called hosts and fill it with all the hostnames on your network like below:

The file can actually have any name, but hosts makes the most sense

This results in both the host and any other docker containers deployed on the host to utilize the DNS service you just deployed. However, if you want the host to ignore the DNS service, but keep the docker containers using it, then keep the dns-nameservers option set to 8.8.8.8 and just update the /etc/default/docker file with the option

DOCKER_OPTS="--dns $MY_SERVER_IP"

Create a docker-compose.yaml file with the following content:

version: "3"

services:
  app:
    container_name: dnsmasq
    image: ${IMAGE_NAME:-dnsmasq}
    restart: always
    ports:
      - "${IP}:53:5353/udp"
      - "${IP}:53:5353/tcp"
    volumes:
      - ./volumes/config/hosts:/dnsmasq.hosts/hosts
    logging:
      driver: "local"
      options:
        max-size: "10m"
        max-file: "5"
        compress: "true"
    environment:
      - LOCAL_DNS_TTL

For years, I was fine just opening up Dnsmasq on port 53 for UDP, but it turns out that I needed to add TCP support when I deployed my own upstream stubby server, in order to make use of DNS over TLS with DNSSEC. This is because DNSSEC responses are often too large to fit into a single UDP packet.

Create the hosts file which will get mounted in the volume

mkdir -p ./volumes/config
touch ./volumes/config/hosts

At this point, it would be good if you filled that hosts file with your A records in a similar format to:

127.0.0.1       localhost
192.168.0.1     gateway.mydomain.com

Create a .env file with the following content:

COMPOSE_PROJECT_NAME="dnsmasq"


# Specify the IP address that the DNS service should be listening on.
# This should be your servers local/private IP address
# You may be able to fetch it with:
# ip addr | grep inet | cut -d/ -f1 | awk '{ print $2}' | grep 192
IP=192.168.1.1


# Sepcify the name of the image, and version that you wish to use.
IMAGE_NAME="programster/dnsmasq:latest"


# Set the TTL for results served up by this, rather than forwarded DNS requests. The default in dnsmasq is 0
# so setting that here, but you may wish to bump it up to something like 300 for a five minute TTL.
LOCAL_DNS_TTL=300

I used to use the sroegner/dnsmasq image, but that wasn't being maintained so I have built my own and configured it automatically update the repository daily.

Now all you need to do is deploy using the Docker Compose tool:

docker compose up

You may wish to add the -d parameter to have it run in the background and not see the output.

Thats it! You now have an incredibly basic DNS "server" setup to answer requests.

You must restart the docker container whenever you make a change to the dnsmasq.hosts/hosts file for the changes to take effect. You can do this by running: docker compose restart

Configure Server To Use The DNS You Just Deployed

Update the network config file at /etc/network/interfaces (or your netplan config if appropriate) so that dns-nameservers option points to the hosts own public IP that it wants to answer requests on. It is a good idea to put in a secondary normal DNS as well for when your new DNS service is not running.

Prevent DHCP Overriding Your Nameserver

If you're doing this on a local home network, chances are that your router is set to provide your computers with dynamic IP's and nameservers. Even if you set a static IP, your server will still grab the nameserver. This will result in your /etc/resolv.conf file having the router's specified nameserver first, which means that your server will not use the DNS system you just set up. Worse yet, this will reset every time the network restarts or you reboot the server.

To prevent this from happening, simply update the DHCP configuration on the server at /etc/dhcp/dhclient.conf, and uncomment/update the following line:

prepend domain-name-servers [YOUR DNS IP HERE];

Alternatively, you could just update your router/dhcp provider's setting for the nameserver so that the correct nameserver automatically "spreads".

References

Last updated: 6th March 2025
First published: 16th August 2018

This blog is created by Stuart Page

I'm a freelance web developer and technology consultant based in Surrey, UK, with over 10 years experience in web development, DevOps, Linux Administration, and IT solutions.

Need support with your infrastructure or web services?

Get in touch