Deploying a PowerDNS Resolver on Docker


Have you been avoiding the seemingly daunting task of deploying a DNS recursor A.K.A. caching server for your internal network? Stress no more because it’s actually very simple, even with a bare metal or virtual machine setup. This tutorial however will not be addressing either of those routes as I believe in pushing methods forward. This is where Docker comes in as it can really speed up your time to deployment on many services, not to mention it can really ease the ongoing maintenance as well! If you aren’t really familiar with the concepts of containers yet then I recommend you take the time to read my introduction to containerization post here. If you don’t already have a working Docker environment to start with, I recommend you read my post on deploying Docker which can be found here.

There are many different container images available for various DNS recursors in the two primary repositories Docker Hub and RedHat Quay. The problem is that many are poorly implemented, not well maintained, and often lack pseudo-standard deployment features that really put that shine on the final product. Often times, even when a product vendor provides their own container images directly, they still are quite lackluster to say the least. For this reason, I will be using container images that I have built myself and are being actively maintained for my own production use.

To get started, be sure to check out the specific image for this tutorial in my GitHub repository which can be found here. This tutorial will assume that you have both a functioning Docker environment as well as the docker-compose tool installed. There are a few ways you can deploy images to a Docker environment but I am going to only cover the docker-compose route as it’s most practical for ongoing maintenance and also translates very well to the Portainer GUI app designed for managing container environments.

Let’s start by creating a YAML formatted file for docker-compose. I will go over some of the various settings in this file following the example so you may want to review those first and tweak the following example accordingly. From a command shell in your Docker environment. execute the following command:

echo 'version: '3.3'
    image: azoriansolutions/powerdns-recursor:latest
    restart: unless-stopped
      - PDNS_allow_from=,
      - PDNS_local_port=53
      - PDNS_local_address=
      - PDNS_any_to_tcp=yes
      - PDNS_api_key=SECURE-API-KEY
      - PDNS_dnssec=validate
      - PDNS_dnssec_log_bogus=yes
      - PDNS_loglevel=3
      - PDNS_webserver=yes
      - PDNS_webserver_address=
      - PDNS_webserver_allow_from=
      - PDNS_webserver_password=SECURE-HTTP-AUTH-PASSWORD
      - "8053:53/udp"
      - "8053:53"
      - "8080:8082"' | tee /tmp/dc-powerdns-recursor.yaml

One of the first and most important settings to take notice of is the “PDNS_allow_from” setting. This defines one or more networks in CIDR format that the recursor will accept DNS queries from. The default value of the setting only allows access from RFC 1918 private IP addresses so you may need to update this if you will be receiving traffic from some public IP networks. You should never set this value to “” unless you know what you’re doing. Otherwise, in many scenarios this can lead to an open resolver which can and will eventually be turned into an attack vector by bad actors. If you are implementing proper firewall filters somewhere along the communication chain, then it would be okay to change this to allow all traffic for ease of configuration maintenance.

Another important setting is the “PDNS_api_key” which controls access to the built-in API server which can be used for issuing various commands to the recursor at runtime. This setting isn’t important if you end up disabling the built-in web-server.

You should definitely take note to the “PDNS_dnssec” setting as this example has configured the most strict form of DNSSEC which is full blown validation. This may have adverse effects depending on how you intend to use the recursor. For more information on this setting, check out the recursor settings page here.

Next up is the “PDNS_webserver” setting. This controls whether or not the built-in web server is enabled. This web server provides a basic page to see real-time performance statistics of the recursor. The web server is also responsible for providing the API server so you must enable it to use the API features.

Also to go with the last setting, if you do enable the web server you may want to consider adjusting the “PDNS_webserver_allow_from” setting. This defines one or more networks in CIDR format which HTTP requests to the web server should be allowed from. If you are performing packet filtering upstream then you are okay to leave this to allow all traffic for ease of configuration maintenance.

Lastly, take notice to the “PDNS_webserver_password” setting which configures a basic password used for HTTP authentication to the real-time statistics page provided by the web server.

Depending on your environment and what you have deployed to the Docker server you’ll be using, you may want to consider changing the port settings:

      - "8053:53/udp"
      - "8053:53"
      - "8080:8082"

The value “8053” defines the host port on the Docker machine to bind the internal container port of 53 to. The other value of “8080” defines the host port on the Docker machine to bind the container’s internal web server port of 8082 to. If you aren’t using any form of reverse proxy to manage ingress traffic to your containerized services and just want to have the Docker machine handle requests directly, then you may consider changing the “8053” values to the standard port of “53”.

You need to be aware that in many environments such as Ubuntu, you can not just change to port 53 alone as the container will fail to start. This is because newer Ubuntu releases have a local resolver running by default on port 53 but it is only bound to the loopback address. Docker has a great feature to work around this challenge though. You can also add a specific IP address before the host port number to keep from binding to all available interfaces on the machine. For example, if your Docker host has a public IP address of “” then you could change the port configuration to “” and “” respectively. This will prevent conflicts with the local resolver that is running by default on Ubuntu.

At this point, you should have tweaked the example configuration either before or after creating the YAML file so presumably you’re ready to deploy the recursor now. Execute the following command:

docker-compose -u /tmp/dc-powerdns-recursor.yaml

Once the deployment process has been completed, you should now have a working recursor ready to serve DNS requests. There are many more settings that you can change to tweak the recursor behavior. For more information, check out the recursor settings page here.

About the author

Add Comment

By Matt


Get in touch

If you would like to contact me, head over to my company website at