Every time your server needs to look up a domain name, it sends a DNS request to another DNS resolver. If it’s asking for the same domains over and over, those repeated requests still have to travel across the network, even though the answer probably hasn’t changed.

For example, imagine a web application that connects to three external APIs every time someone visits your site. If your server handles thousands of requests a day, it also ends up performing those same DNS lookups thousands of times.

That’s unnecessary network traffic and adds a small delay to every request. A local caching DNS resolver solves this problem by storing recently used DNS records and reusing them until they expire. On Rocky Linux 10, you can set one up with Unbound in about ten minutes.

Unbound is a lightweight, validating, recursive DNS resolver developed by NLnet Labs. Unlike BIND or PowerDNS, it isn’t designed to host DNS zones. Its main job is to resolve DNS queries, cache the results in memory, and return cached answers instantly when the same domain is requested again.

The steps in this guide work the same on Rocky Linux 10, RHEL 10, and AlmaLinux 10. All three distributions provide the same unbound package through dnf, use the same configuration files, and behave almost identically once the service is installed and running.

TecMint Weekly Newsletter

Get the Learn Linux 7 Days Crash Course free when you join 34,000+ Linux professionals reading every Thursday.

Check your email for a magic link to get started.

Something went wrong. Please try again.

Lab Setup

For this guide, we’ll use two Rocky Linux 10 systems.

  • DNS Server: 192.168.1.50 (resolver.tecmintlocal.com).
  • Client Machine: 192.168.1.75 (app01.tecmintlocal.com).

The DNS server will run Unbound, while the client will use it for DNS lookups.

Before installing anything, make sure the DNS server has the correct hostname and a static IP address. Since clients will always connect to this server for DNS queries, its IP address should remain the same. If it changes, clients won’t be able to reach the resolver until their DNS settings are updated.

Run the following commands on the DNS server to verify its hostname and IP address:

hostnamectl
ip -4 addr show

You should see the server hostname set to resolver.tecmintlocal.com and the network interface assigned the IP address 192.168.1.50. If your environment uses different values, simply replace the hostnames and IP addresses throughout this guide with your own.

Step 1: Install Unbound

Start by updating your system packages, then install Unbound along with the bind-utils package.

sudo dnf update -y
sudo dnf install -y unbound bind-utils

The bind-utils package includes the dig command, which is one of the most useful tools for testing DNS. We’ll use it later to verify that Unbound is resolving queries correctly and serving cached results.

Before making any changes, it’s also a good idea to back up the default Unbound configuration file. If you accidentally make a mistake while editing the configuration, you can quickly restore the original file instead of reinstalling the package.

sudo cp /etc/unbound/unbound.conf /etc/unbound/unbound.conf.orig

If this helped you, who’s still troubleshooting DNS latency without a local resolver.

Step 2: Configure Unbound

Open the Unbound configuration file in your preferred text editor.

sudo vi /etc/unbound/unbound.conf

Inside the server: section, add or update the following settings:

server:
    interface: 192.168.1.50
    interface: 127.0.0.1
    port: 53

    do-ip4: yes
    do-udp: yes
    do-tcp: yes

    access-control: 127.0.0.0/8 allow
    access-control: 192.168.1.0/24 allow
    access-control: 0.0.0.0/0 refuse

    hide-identity: yes
    hide-version: yes

    verbosity: 1
    logfile: "/var/log/unbound.log"
    use-syslog: no

Here’s what these settings do:

  • interface specifies the IP addresses where Unbound listens for DNS requests. In this example, it listens on the server’s LAN IP (192.168.1.50) and the local loopback address (127.0.0.1), which allows both the server itself and other machines on your local network to use the resolver.
  • do-ip4, do-udp, and do-tcp enable IPv4 and allow Unbound to accept DNS queries over both UDP and TCP, which are the standard DNS transport protocols.
  • access-control determines which clients are allowed to use your DNS server. Here, only the local machine and devices on the 192.168.1.0/24 network can send DNS queries.
  • hide-identity and hide-version prevent Unbound from revealing its identity and version number when someone performs special DNS queries. While not essential, these options provide a small security benefit by exposing less information about your server.
  • verbosity, logfile, and use-syslog control logging. Setting verbosity to 1 provides basic operational logs, and storing them in a dedicated log file makes troubleshooting easier.

Note: On Rocky Linux 10, Unbound supports DNSSEC validation out of the box. It automatically validates signed DNS responses using the root trust anchor, so you don’t need any additional DNSSEC configuration in most cases.

Configure Forwarders

By default, Unbound can perform full recursive DNS lookups by contacting the root DNS servers. For many environments, it’s simpler and often faster to forward requests to trusted upstream DNS providers instead.

Add the following section at the end of the configuration file:

forward-zone:
    name: "."
    forward-addr: 1.1.1.1
    forward-addr: 9.9.9.9

In this example:

  • 1.1.1.1 is Cloudflare’s public DNS server.
  • 9.9.9.9 is Quad9’s public DNS server.

If the first server is unavailable, Unbound automatically tries the next one.

If you want to go deeper into securing services like this one, the SSH Course on Pro TecMint covers access control, key-based hardening, and lockdown patterns you’ll reuse on every server you manage.

Step 3: Resolve Any Port 53 Conflicts

Before starting Unbound, make sure that no other service is already using port 53, which is the standard port for DNS.

On Rocky Linux, systemd-resolved is enabled by default and often creates a local DNS stub listener on 127.0.0.53:53. If that port is already in use, Unbound won’t be able to start.

To check which service is using port 53, run:

sudo ss -tulpn | grep :53

If you see systemd-resolved listening on port 53, disable only its DNS stub listener. This frees the port for Unbound while allowing systemd-resolved to continue handling other system functions.

Create a configuration file with the following setting:

sudo mkdir -p /etc/systemd/resolved.conf.d
echo -e "[Resolve]\nDNSStubListener=no" | sudo tee /etc/systemd/resolved.conf.d/no-stub.conf
sudo systemctl restart systemd-resolved

After restarting the service, check port 53 again:

sudo ss -tulpn | grep :53

If nothing is listening on port 53, Unbound will be able to bind to it when you start the service in the next step.

Tip: If another DNS service such as BIND (named) or dnsmasq is using port 53, stop or reconfigure that service before starting Unbound. Only one application can listen on the same IP address and port at a time.

Step 4: Validate and Start Unbound

Before starting the service, check the configuration file for syntax errors, which helps you catch any mistakes before Unbound tries to load the configuration.

sudo unbound-checkconf

If the configuration is valid, the command returns:

unbound-checkconf: no errors in /etc/unbound/unbound.conf

If you see any error messages, Unbound will usually tell you the line number where the problem occurred. Open the configuration file, correct the error, and run the command again until no errors are reported.

Once the configuration passes validation, start the Unbound service and enable it to start automatically whenever the system boots:

sudo systemctl enable --now unbound

Next, verify that the service is running:

sudo systemctl status unbound

If everything is working correctly, you should see the service in the active (running) state.

● unbound.service - Unbound DNS server
     Loaded: loaded (/usr/lib/systemd/system/unbound.service; enabled)
     Active: active (running) since ...

If the service fails to start, review the status output for error messages. You can also check the log file you configured earlier or view the system journal for more detailed information:

sudo journalctl -u unbound --no-pager

Step 5: Allow DNS Traffic Through the Firewall

If firewalld is enabled, you’ll need to allow incoming DNS traffic so that other systems on your network can use the Unbound server.

Run the following commands:

sudo firewall-cmd --add-service=dns --permanent
sudo firewall-cmd --reload

To verify that the rule has been added successfully, run:

sudo firewall-cmd --list-services

If everything is configured correctly, you should see dns listed along with any other services that are already allowed, for example:

cockpit dhcpv6-client dns ssh

At this point, your firewall is configured to accept DNS requests from the clients allowed by your Unbound configuration.

Step 6: Verify That DNS Caching Is Working

Now it’s time to confirm that Unbound is actually caching DNS responses. From the DNS server, query a domain using dig and point it directly to your Unbound server:

dig tecmint.com @192.168.1.50

Look for the Query time field in the output. The first lookup usually takes longer because Unbound has to contact the upstream DNS servers to resolve the domain.

For example:

;; Query time: 68 msec
;; SERVER: 192.168.1.50#53(192.168.1.50)

Now run the same command again:

dig tecmint.com @192.168.1.50

This time, the response should be much faster because Unbound can return the answer from its cache instead of performing another external DNS lookup.

For example:

;; Query time: 0 msec
;; SERVER: 192.168.1.50#53(192.168.1.50)

The exact query times will vary depending on your network and upstream DNS servers, but the second lookup should be noticeably faster than the first. A query time of 0 ms or 1 ms is common when the answer is served from the local cache.

You can also test with a different domain, such as:

dig google.com @192.168.1.50
dig github.com @192.168.1.50

Run each command twice and compare the query times. The first lookup retrieves the DNS record from the upstream resolver, while the second lookup is typically served directly from Unbound’s cache, demonstrating that DNS caching is working as expected.

Step 7: Configure a Client to Use the Unbound DNS Server

With the DNS server up and running, the final step is to configure a client machine to use it for DNS lookups.

If you’re using NetworkManager, set your Unbound server (192.168.1.50) as the preferred DNS server for the network connection.

First, list the available network connections:

nmcli connection show

Note the name of the active connection (for example, “Wired connection 1“), then run:

sudo nmcli connection modify "Wired connection 1" ipv4.dns "192.168.1.50"
sudo nmcli connection modify "Wired connection 1" ipv4.ignore-auto-dns yes
sudo nmcli connection up "Wired connection 1"

These commands configure the client to use your Unbound server for DNS resolution instead of the DNS servers provided automatically by your router or DHCP server.

Display the contents of /etc/resolv.conf:

cat /etc/resolv.conf

You should see your Unbound server listed, for example:

nameserver 192.168.1.50

Now test DNS resolution from the client:

dig google.com

In the output, look for the SERVER field. It should show your Unbound server:

;; SERVER: 192.168.1.50#53(192.168.1.50)

You can also test with a few additional domains:

dig github.com
dig tecmint.com

If the queries complete successfully and the SERVER field points to 192.168.1.50, your client is now using Unbound as its DNS resolver.

From this point on, repeated DNS lookups for the same domains will be served from Unbound’s cache whenever possible, reducing lookup times and minimizing unnecessary requests to upstream DNS servers.

Managing and Troubleshooting Unbound

A handful of unbound-control commands cover most day-to-day maintenance.

  • sudo unbound-control status shows uptime, version, and whether the server is answering queries.
  • sudo unbound-control stats_noreset | grep total shows total queries handled and cache hit counts without resetting the counters.
  • sudo unbound-control dump_cache > /tmp/dns_cache_backup.txt writes the full cache out to a file, useful before a planned restart.
  • sudo unbound-control lookup tecmint.com shows which forwarder answered a specific domain and whether it’s currently cached.
  • sudo unbound-control flush tecmint.com removes a single cached record without touching anything else.
  • sudo unbound-control flush_zone tecmintlocal.com clears every cached record under a specific zone, handy when you’ve just changed internal DNS records and don’t want to wait out the TTL.

If a client reports it can’t resolve anything, check journalctl -u unbound -f first, because most failures trace back to either the access-control list not including the client’s subnet, or the forward zone pointing at an upstream resolver that’s unreachable from your network.

Warning: Never set access-control: 0.0.0.0/0 allow on a server with a public IP. That turns Unbound into an open resolver that anyone on the internet can abuse for DNS amplification attacks against a third party.

Want a deeper dive into diagnosing service failures like this one? The Claude Code for Linux Sysadmins course walks through systemd debugging and log triage step by step.
Conclusion

You’ve now set up Unbound as a local caching DNS resolver on Rocky Linux 10. From this point on, repeated DNS requests for the same domains are served directly from the local cache instead of being sent to upstream DNS servers every time.

This reduces DNS lookup times, lowers unnecessary network traffic, and can improve the responsiveness of applications that frequently access the same external services.

Have you run Unbound in production, or are you still relying on your ISP’s resolver? Tell us what pushed you one way or the other in the comments.

If this article helped, with someone on your team.

TecMint Weekly Newsletter

Get the Learn Linux 7 Days Crash Course free when you join 34,000+ Linux professionals reading every Thursday.

Check your email for a magic link to get started.

Something went wrong. Please try again.

By admin

Leave a Reply

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