• January 13, 2026

How to Deploy Unbound – Validating DNS Resolver

How to Deploy Unbound – Validating DNS Resolver

Deploy a secure, validating Unbound DNS resolver on Ubuntu 24.04 using Docker Compose.

Unbound is a validating, recursive, and caching DNS resolver. Unlike some other DNS servers that rely on heavy web interfaces, Unbound is designed to be lean, fast, and secure. It rigorously validates DNSSEC signatures to ensure the authenticity of the data it serves, making it a preferred choice for privacy-conscious users and enterprise environments.

This article demonstrates how to deploy Unbound DNS Server on Ubuntu 24.04 using Docker Compose.

Prerequisites

Before you begin:

  • Have access to an Ubuntu 24.04 server as a non-root user with sudo privileges.
  • Install Docker and Docker Compose.

Freeing Port 53

DNS servers communicate using port 53. By default, Ubuntu 24.04 runs its own internal DNS service called systemd-resolved on this port. You must disable this internal service so that Unbound can take control of port 53.

Additionally, you need to ensure the server itself can still connect to the internet to download updates, even if Unbound is stopped. To do this, you will configure the server to use a public DNS provider.

  1. Stop the systemd-resolved service.
    console
    $ sudo systemctl stop systemd-resolved
  2. Disable the service so it does not start again when you reboot.
    console
    $ sudo systemctl disable systemd-resolved
  3. Remove the existing DNS configuration file.
    console
    $ sudo rm /etc/resolv.conf
  4. Create a new DNS configuration file.
    console
    $ echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf

Set Up the Directory Structure and Configuration File

Unbound relies on a configuration file to define its behavior, access controls, and security features. You must create the necessary directory structure to store this file.

  1. Create the project directory.
    console
    $ mkdir -p ~/unbound
  2. Navigate to the directory.
    console
    $ cd ~/unbound
  3. Create the unbound.conf configuration file.
    console
    $ nano unbound.conf
  4. Add the following configuration content. Replace YOUR_CLIENT_IP with the public IP address of the client device that will query this server. To allow multiple clients, add additional access-control lines.
    yaml
    server:
      interface: 0.0.0.0
      interface: ::0
      port: 53
    
      access-control: 127.0.0.0/8 allow
      access-control: 192.168.0.0/16 allow
      access-control: 172.16.0.0/12 allow
      access-control: 10.0.0.0/8 allow
      access-control: YOUR_CLIENT_IP/32 allow
      access-control: 0.0.0.0/0 refuse
    
      hide-identity: yes
      hide-version: yes
      use-caps-for-id: yes
      prefetch: yes
    
      num-threads: 2
      msg-cache-slabs: 4
      rrset-cache-slabs: 4
      infra-cache-slabs: 4
      key-cache-slabs: 4
      rrset-cache-size: 100m
      msg-cache-size: 50m
      so-rcvbuf: 1m
    
    remote-control:
      control-enable: no

    Save and close the file.

    This configuration defines Unbound’s security posture, access rules, and performance behavior:

    • Listening Interfaces: Unbound listens on all IPv4 and IPv6 interfaces so it can accept DNS queries from allowed clients.
    • Access Control: Controls who may query your resolver.
      • Private ranges allow internal communication (including Docker networks).
      • YOUR_CLIENT_IP/32 represent individual clients.
      • Everything else (0.0.0.0/0) is explicitly denied for safety.
    • Security Settings:
      • hide-identity and hide-version prevent Unbound from revealing software details.
      • use-caps-for-id adds DNS query randomization to prevent spoofing attacks.
      • prefetch refreshes items in cache before expiration.
    • Performance Tuning: Optimized cache sizes, slab counts, and thread counts improve speed and reduce latency on small-to-medium servers.
    • Remote Control Disabled: Prevents external control commands for increased security.

Deploy with Docker Compose

This section sets up the Unbound deployment using the popular mvance/unbound Docker image.

  1. Create the Docker Compose manifest file.
    console
    $ nano docker-compose.yml
  2. Add the following contents. Replace UTC with your preferred timezone.
    yaml
    services:
      unbound:
        image: mvance/unbound:latest
        container_name: unbound
        restart: unless-stopped
        environment:
          TZ: UTC
        ports:
          - "53:53/tcp"
          - "53:53/udp"
        volumes:
          - ./unbound.conf:/opt/unbound/etc/unbound/unbound.conf:ro

    Save and close the file.

    This Docker Compose configuration deploys a standalone Unbound server that listens directly on standard DNS ports. Each setting plays a specific function in the deployment:

    unbound service

    • Runs the container using the community-maintained mvance/unbound image.
    • Exposes ports 53/tcp and 53/udp to allow standard DNS queries from your allowed clients.
    • Mounts your local unbound.conf file into the container at /opt/unbound/etc/unbound/unbound.conf. The :ro flag ensures the container reads the file as read-only, preventing accidental changes from inside the container.
    • Restarts automatically (unless-stopped) to ensure high availability after server reboots.
  3. Start the service in detached mode.
    console
    $ docker compose up -d
  4. Check the container status.
    console
    $ docker compose ps

Testing

Verify that the Unbound server is functioning correctly by querying it directly.

  1. Run this command from your client’s terminal. Replace SERVER_IP with your server’s public IP address.
    console
    $ dig @SERVER_IP vultr.com

    You should see a standard DNS response with an A record of vultr.com

Conclusion

You have successfully deployed Unbound DNS Server on Ubuntu 24.04. You now have a high-performance, validating recursive resolver. You can further customize the unbound.conf file to add local zones, blocklists, or advanced caching rules.

Leave a Reply

Your email address will not be published. Required fields are marked *