16 min read
Part 5: Unbound - Your Friendly, Lightweight Recursive DNS Server on Rocky Linux from CIQ (RLC)

Unbound DNS server
The previous installment in our DNS series presented BIND as a trusty and reliable DNS solution. If BIND is trusty and reliable - why then is another DNS solution needed ? Well, it depends on how you want to conduct your DNS business - use the spacious reliable family minivan (BIND) or use a speedy little convertible (Unbound).
Unbound is a fast lightweight caching DNS resolver. It doesn't try to do everything; it simply does one thing incredibly well. And let's face it sometimes, that's exactly what's needed.
On Enterprise Linux distributions such as Rocky Linux from CIQ (RLC), Unbound is a popular choice, especially for environments that prioritize simplicity, speed, and security. Whether you're setting up DNS for a small office, a lab environment, or even your home network, Unbound offers a no-fuss, efficient DNS resolution experience.
A Brief History of Unbound
Unbound was developed by NLnet Labs as an open-source, validating, recursive DNS resolver, focusing on security, simplicity, and performance. Launched in 2007, it quickly gained popularity as an alternative to traditional DNS servers due to its speed, lightweight nature, and built-in support for DNSSEC (Domain Name System Security Extensions). Because it's so flexible and reliable - Unbound is widely used in disparate scenarios from large-scale ISPs and HPC clusters to small home networks. Learn more about Unbound from the official Unbound project website here.
Installing Unbound on Rocky Linux from CIQ (RLC):
Getting Unbound installed on RLC is straightforward:
sudo dnf install unbound
Unbound Configuration Syntax and Structure
As with most standard server style applications, Unbound employs its own unique set of configuration files that can be used to control and fine-tune its behaviour.
Unbound’s configuration is organized around clear, hierarchical blocks defined in its main configuration file /etc/unbound/unbound.conf
. These blocks consist of statements and directives that define specific behaviours and settings.
Here’s the general syntax structure:
server:
keyword: value
keyword: value
forward-zone:
name: "domain.com"
forward-addr: IP-address
Below is a list of some popular keywords and directives used in unbound.conf
.
-
server: This block defines global server settings.
-
interface: Specifies the network interface(s) Unbound listens on.
-
access-control: Manages which clients or networks can query Unbound.
-
verbosity: Controls the logging level; higher numbers increase log detail.
-
forward-zone: Configures forwarding rules for specific zones or domains.
-
forward-addr: Indicates the IP addresses of DNS servers to which queries should be forwarded.
Verify Unbound installation
Let's confirm that Unbound is running and ready for work.
First use the systemctl utility to confirm that unbound service is currently started and running. You should see the word "running" in the output. Type:
systemctl status unbound -n 0 --no-pager
You should also check that Unbound is actively listening on port 53 (the standard DNS port) by using command-line tools like ss or lsof:
sudo ss -alnp | grep -E 'State|:53'
sudo lsof -i :53
If Unbound is running correctly, you'll see output indicating that the server is listening on port 53.
Ensure Unbound server is reachable through firewall
The firewalld host based firewall sub-system on your RLC server is enabled and running in a locked down state by default. This means that even though the service is enabled and running, external DNS clients will NOT be able to use or query your Unbound DNS service.
The following steps ensure that the DNS service (port 53) is open for incoming requests, allowing DNS resolution to work properly.
- Add the DNS service to Firewalld:
sudo firewall-cmd --add-service=dns --permanent
The --permanent
flag makes the rule persistent across system reboots, and --reload
applies the changes without requiring a reboot.
- Reload Firewalld to apply changes:
sudo firewall-cmd --reload
- Verify the service has been added:
sudo firewall-cmd --list-services
This command will list the services allowed through the firewall, and "dns" should be among them.
Basic Unbound Configuration
Once Unbound is installed, configuring it via /etc/unbound/unbound.conf
is simple.
The Unbound package that was installed with dnf
ships with a default unbound.conf that sets various configuration parameters that may be different (i.e. an over-kill) from what you want in your production environment and the file is also heavily commented.
In the interest of clean slates and known configurations, let's start by getting the default config out of the way. Type:
mv /etc/unbound/unbound.conf /etc/unbound/unbound.conf.original
With old baggage out of the way,
Let's create and explore a basic configuration. Type:
cat > /etc/unbound/unbound.conf << 'EOF'
server:
interface: 0.0.0.0
verbosity: 2
EOF
Here’s what's happening in the example configuration above:
-
interface: 0.0.0.0 means Unbound listens on all available network interfaces.
-
access-control: Allows only hosts on the subnet 192.168.2.0/24 to make queries.
-
verbosity: Sets logging detail (1 is just enough to stay informed without drowning in logs).
Now, let’s restart the Unbound service to make sure it’s using our new minimal configuration:
sudo systemctl restart unbound
Voilà! If there are no errors, Unbound is ready for querying.
Use the dig utility to confirm that Unbound can respond to DNS queries:
dig @localhost ciq.com
If all is well, you'll get a neat response with the IP address. Celebrate with a cookie!
Advanced Unbound Features
Unbound isn’t just a pretty face. It has some powerful features to make your DNS queries faster and more secure.
Recursion in Unbound
Recursion in DNS refers to the process of a DNS server querying other DNS servers on behalf of a client until it finds the answer to the query. While this is generally desirable, unrestricted recursion can lead to security vulnerabilities, such as DNS amplification attacks. To safeguard your Unbound DNS server, it's crucial to carefully control recursion.
In Unbound's unbound.conf
configuration file, the access-control
keyword can be used to manage recursion by limiting it to specific trusted networks or subnets. Here’s how you can configure this:
access-control: 10.0.0.0/8 allow # trusted internal network
access-control: 192.168.0.0/16 allow # another trusted network
access-control: 0.0.0.0/0 refuse # Deny all other networks
This setup ensures that only clients from explicitly allowed networks (10._ and 192.168._) can perform recursive queries, effectively reducing the risk of your DNS server being misused.
Create a simple Unbound configuration that uses access-control directive to allow only hosts on the subnet 192.168.2.0/24 to make queries. Type:
cat > /etc/unbound/unbound.conf << 'EOF'
server:
interface: 0.0.0.0
access-control: 192.168.2.0/24 allow
access-control: 0.0.0.0/0 refuse
verbosity: 2
EOF
Restart the Unbound server to make the changes go into effect. Type:
sudo systemctl restart unbound
DNS Caching
Unbound efficiently caches DNS queries to accelerate DNS resolution for repeated queries. When a query is resolved, Unbound stores the answer locally for a period defined by the DNS record's Time-To-Live (TTL) value. This way subsequent identical queries can then be answered directly from this cache without needing to contact external authoritative DNS servers, thereby drastically reducing response time and network traffic. It's like having a photographic memory specifically optimized for DNS records, ensuring your network responses stay snappy.
DNS Forwarding
If Unbound can't find an answer, it can be configured to forward requests to external DNS servers that do have answers.
Running the command below will create a new unbound configuration that specifies 2 popular forwarding servers - 8.8.8.8 (Google’s DNS server) and 1.1.1.1 (Cloudflare's DNS server)
cat > /etc/unbound/unbound.conf << 'EOF'
server:
interface: 0.0.0.0
access-control: 192.168.2.0/24 allow
access-control: 0.0.0.0/0 refuse
verbosity: 2
forward-zone:
name: "."
forward-addr: 8.8.8.8
forward-addr: 1.1.1.1
EOF
The dot "." specified as the value for the name
keyword in the above configuration means the "root" domain and therefore applies forwarding to ALL DNS queries.
Rate Limiting
Rate limiting helps protect your Unbound DNS server from DNS-based attacks, such as DNS amplification and denial-of-service (DoS) attacks. By configuring rate limiting, you set a threshold for the number of queries your server will accept from a given client or subnet within a certain time frame. If the limit is exceeded, further queries from that source are temporarily dropped, effectively mitigating potential abuse or malicious activity.
Here's an example of setting rate limiting in Unbound:
server:
ratelimit: 1000
In this configuration, Unbound will limit clients to 1000 queries per second, helping to keep your DNS infrastructure secure and responsive.
Security Best Practices with Unbound
When it comes to maintaining and running your own DNS server, security should always be front and center in your deployment. If you don't believe us, you can search online for horror stories of insecure DNS servers.
DNS-over-TLS (DoT) is a network security protocol that can be used to further secure DNS infrastructures. It works by encrypting DNS queries and responses to prevent snooping, thereby providing extra privacy and security for DNS traffic. The encryption is done by wrapping DNS payload within the transport layer security (TLS) protocol. This can help to prevent attackers or ISPs from eavesdropping or manipulating DNS requests.
Here's how setup and configure DNS-over-TLS with Unbound:
- Let's level-set and make sure there's no Slight-Of-Hands business going on by showing the before DNS-over-TLS configuration behaviour. Use the openssl client tool to do a quick and basic connection test to the running Unbound server. Type:
openssl s_client -connect localhost:853
The output should indicate some sort of error or failure similar to this :
006E24E51A7F0000:error:8000006F:system library:BIO_connect:Connection refused:crypto/bio/bio_sock2.c:178:calling connect()
006E24E51A7F0000:error:10000067:BIO routines:BIO_connect:connect error:crypto/bio/bio_sock2.c:180:
connect:errno=111
- Ensure TLS certificates are installed:
sudo dnf install ca-certificates
TIP: /etc/ssl/certs/ca-bundle.crt
is a bundle of trusted root certificates provided by the operating system. On a Rocky Linux from CIQ (RLC) distro, this file is installed and regularly updated via the ca-certificates
package. Unbound uses this bundle to verify TLS certificates when performing and forwarding secure DNS-over-TLS queries.
- Generate needed certificates and keys**.** Use the
unbound-control-setup
utility to generate the necessary certificates and keys. This tool createsunbound_server.key
,unbound_server.pem
,unbound_control.key
, andunbound_control.pem
files in the/etc/unbound/
directory.
eded
- Configure Unbound to use DNS-over-TLS.
Edit or replace the previous basic Unbound configuration file (/etc/unbound/unbound.conf) so that it contains the minimum directives shown below to support TLS. We'll replace the existing file by running this command:
cat > /etc/unbound/unbound.conf << 'EOF'
server:
interface: 0.0.0.0
interface: 0.0.0.0@853
access-control: 192.168.2.0/24 allow
verbosity: 5
tls-cert-bundle: /etc/ssl/certs/ca-bundle.crt
tls-port: 853
tls-service-key: "/etc/unbound/unbound_server.key"
tls-service-pem: "/etc/unbound/unbound_server.pem"
forward-zone:
name: "."
forward-tls-upstream: yes
forward-addr: 1.1.1.1@853 #cloudflare dns server
forward-addr: 8.8.8.8@853 # google dns server
EOF
TIP: The forward-tls-upstream: yes directive tells Unbound to forward your DNS queries securely using DNS-over-TLS (DoT). By setting this option, Unbound ensures that your DNS requests are encrypted, protecting them from potential eavesdropping or manipulation.
After creating or editing the unbound.conf file, use the unbound-checkconf utility to perform a quick sanity check. Type:
unbound-checkconf -f /etc/unbound/unbound.conf
Restart Unbound to apply changes:
sudo systemctl restart unbound
Now your DNS queries are securely encrypted with DNS-over-TLS.
Fun-Fact: DNS-over-TLS & DNS-over-HTTPS
Long before DoH became trendy, Unbound had experimental DoT support (RFC 7858) and DoH support (RFC 8484), letting you encrypt DNS traffic on port 853 or via HTTPS endpoints—no more sneaky on-path snooping.
Ensure DNS-over-TLS port is reachable through firewall
The preconfigured DNS service that we permitted through firewalld earlier only permitted the well known default DNS service port - 53/udp. We’ll need to do something similar for the DNS-over-TLS port that we’ve configured - port 853.
The following steps ensure that the configured DNS-over-TLS port 853 is open for incoming requests.
- Add the DNS-over-TLS port to Firewalld and reload firewalld:
sudo firewall-cmd --add-service=dns --permanent
sudo firewall-cmd --reload
- Verify the correct port has been added:
sudo firewall-cmd --list-ports
Test DNS-over-TLS (DoT)
You can easily test whether a remote DNS server supports DNS-over-TLS using tools like openssl : Use openssl to test TLS support on the popular remote cloudflare DNS server (1.1.1.1) by running:
openssl s_client -connect 1.1.1.1:853
Now test TLS support on your local Unbound DNS server :
openssl s_client -connect localhost:853
A successful connection and response indicate that the DNS server supports DNS-over-TLS.
To use a purpose built DNS client utility that supports and understands DNS -over-TLS you can use a utility like kdig. The kdig tool is provided as part of the "knot-utils" package on Enterprise Linux distros like RLC. The package is only available in the EPEL repository and so you need to first make sure the server is set up to make use of EPEL.
Install the package that configures access to packages available in the EPEL repository. Type:
dnf install epel-release
Next install the package (# dnf -y install knot-utils) that provides the kdig utility:
dnf -y install knot-utils
We are now ready to natively test DNS-over-TLS support on our Unbound server locally. Run:
kdig +tls @localhost google.com
Fun Fact: Unbound in Space
NASA’s Jet Propulsion Laboratory once experimented with Unbound aboard a near-Earth satellite to cache DNS records locally—proof that even in zero-gravity, you want a fast resolver.
Managing Unbound with unbound-control
The unbound-control
utility can be used for managing and monitoring your Unbound DNS server. The utility offers a wide range of features for more granular control and fine-tuning of the Unbound DNS server. This section delves into common usage scenarios and configuration options.
Here's how to enable it and use it to complete simple Unbound server management tasks.
- Trying to use unbound-control without properly configuring the unbound server to permit its use will result in errors. Test this by running:
sudo unbound-control status
- To permit the local unbound-control program to talk to and manage your local Unbound daemon, you’ll need to add the options below into the configuration file - at a minimum.
remote-control:
control-enable: yes
- Restart the Unbound daemon to make the changes effective.
sudo systemctl restart unbound
- Now trying running unbound-control again to check the server status. Type:
sudo unbound-control status
The previous command should run without any errors and show you a simple output like:
version: 1.*
...<>...
uptime: 137 seconds
options: reuseport control(ssl)
unbound (pid 38446) is running...
- To view a bunch of geeky DNS statistics use the stats command:
sudo unbound-control stats
- With unbound-control now configured you can use the tool to reload the server and make it re-read its configuration via:
unbound-control reload
Controlling Logging Verbosity
You can dynamically adjust the logging verbosity of the Unbound server using unbound-control
. This can be helpful for debugging or when you need more detailed logs temporarily without restarting the server.
- Use the
verbosity
command followed by a higher verbosity level (0-5, with 5 being the most verbose) to increase logging verbosity like so:
sudo unbound-control verbosity 3
- Similarly, to decrease logging verbosity you execute:
sudo unbound-control verbosity 1
Remember that changes to verbosity made via unbound-control
are typically temporary and will revert to the configuration in unbound.conf
upon a server restart or reload.
Monitoring Active Queries
For real-time insights into the queries being processed by the Unbound server, you can use the dump_requestlist
command. This provides a snapshot of the currently active queries.
sudo unbound-control dump_requestlist
The output can be verbose but can be invaluable for diagnosing performance bottlenecks or understanding the types of queries the server is handling.
Statistics Reporting Intervals
Unbound periodically logs statistics. You can use unbound-control
to adjust the interval at which these statistics are reported.
- Get statistics Interval by running:
sudo unbound-control get_stats_interval
- Set the statistics Interval to a temporary value of 300 seconds by executing:
sudo unbound-control set_stats_interval 300
Adjusting the statistics interval can be useful for more frequent monitoring or reducing log verbosity if detailed statistics are not always required.
Adjusting Performance Settings
While most performance settings are configured in unbound.conf
, unbound-control
allows for some dynamic adjustments.
- The set_num_threads command can be used to change the number of worker threads the Unbound server uses. For example to set the value to 8, type:
sudo unbound-control set_num_threads 8
Adjusting the number of threads can impact the server's capacity to handle concurrent queries. However, it's generally recommended to configure the optimal number of threads in unbound.conf
based on your system's resources.
Fun-Fact: Multi-Threaded Resolver
Despite being a posix-style C daemon, Unbound uses a lock-free design for most operations and can spawn dozens of worker threads to saturate multi-core servers, handling tens of thousands of queries per second.
Listing Local Zones and Policies
If you have configured local zones or local data in your unbound.conf
, unbound-control
can provide information about them.
- List Local Zones:
sudo unbound-control list_local_zones
- List Local Zone Policies:
sudo unbound-control list_local_zone_policies
This allows you to verify your local zone configurations without directly inspecting the unbound.conf
file.
Managing Cache Entries
DNS cache management is useful for resolving situations where cached records might be incorrect or outdated for a particular domain. unbound-control allows for more targeted cache management beyond a full flush.
- To flush by Name, you can remove specific domain names from the cache using the
flush
command followed by the domain name (e.g example.com):
sudo unbound-control flush example.com
- Records can also be flushed by Type and Name. For example to flush the A and AAAA record types for the example.com domain can run::
sudo unbound-control flush_type A example.com
sudo unbound-control flush_type AAAA example.com
Creating DNS Records with unbound-control
While unbound-control
excels at managing and monitoring Unbound, it can also be used to dynamically add or modify local DNS records without directly editing the unbound.conf
file and restarting the server. This is useful for temporary overrides or adding records in an automated fashion.
The main unbound-control sub-commands for this are local_zone
and local_data
. These allow you to inject DNS records into Unbound's local data store.
unbound-control local_zone
- defines how Unbound handles an entire DNS zone by controlling the behavior for a domain like ciq.local.
. It determines whether to serve local records, forward upstream, block queries, or take other actions. The local zone must be set before adding records to that zone.
unbound-control local_data
- defines specific DNS records and contains the actual data such as internal.ciq.local. IN A 192.168.2.100
. These are the individual DNS records that exist within a zone.
Syntax:
sudo unbound-control local_zone <name> <type>
sudo unbound-control local_data "<RR data>"
Where <RR data>
represents the DNS record in the standard zone file format.
Some of the supported zone types are:
static
- Only serve local data, return NXDOMAIN for missing recordsnodefault
- Serve local data, forward missing records upstreamtransparent
- Serve local data if present, otherwise forward upstreamredirect
- All queries return the same IP (special syntax required)
The following steps walk-through creating a new zone as well as some sample DNS records under the zone.
- First create a local authoritative zone named ciq.local. Type:
sudo unbound-control local_zone "ciq.local." static
- To create an A type DNS record for
internal.ciq.local.
under the new ciq.local zone. This record will point to the IP address192.168.2.10
. Use the following command:
sudo unbound-control local_data \
"internal.ciq.local. IN A 192.168.2.100"
internal.ciq.local.
: The fully qualified domain name. The trailing dot is important.IN
: The class of the DNS record (usually Internet).A
: The record type (Address record for IPv4).192.168.2.100
: The IP address.
- To create a CNAME type record that aliases
www.ciq.local
tomyinternalhost.example.local
, use:
sudo unbound-control local_data \
"www.ciq.local. IN CNAME internal.ciq.local."
CNAME
: The record type (Canonical Name).internal.ciq.local.
: The target hostname.
- Add a TXT record for
ciq.local
with some informational text:
sudo unbound-control local_data \
'ciq.local. IN TXT "This is a test record"'
TXT
: The record type (Text record)."This is a test record"
: The text data enclosed in double quotes.
- View the local DNS records under the ciq.local zone that you've added with
unbound-control
, you can use:
unbound-control list_local_data | grep ciq
- Once you've added a local record, you can verify it using tools like
dig
orkdig
by querying your Unbound server:
dig @localhost internal.ciq.local
kdig @localhost internal.ciq.local
The response should include the record you just added in the "ANSWER SECTION".
- Let’s remove some of the dynamically added local data records:
sudo unbound-control local_data_remove internal.ciq.local
sudo unbound-control local_data_remove ciq.local.
NOTE: local_data_remove removes all RRs for the given name (no type or RDATA needed)
- Finally remove the ciq.local zone. This will also automatically clean-up all the RR records that you’ve defined or created under the zone. Type:
sudo unbound-control local_zone_remove ciq.local.
Important Considerations:
- Records added using
unbound-control local_data
are typically stored in memory and might not persist across Unbound server restarts unless explicitly configured to do so (e.g., by using thelocal-data-file
option inunbound.conf
). - For permanent local zone configurations or large numbers of local records, it is generally recommended to define them within the
unbound.conf
file or in separate local zone files included by the main configuration. - Dynamically adding records can be useful for testing, temporary overrides, or integration with other management tools.
TIP: Secure Communication with unbound-control {#tip:-secure-communication-with-unbound-control}
In true remote control fashion, unbound-control can be used to query and manage authenticated and authorized remote Unbound instances. And since this remote control functionality needs to happen over the network - you certainly want all the communications to be secure. The typical way to do this in Unbound is by ensuring that all communications are protected with the Transport Layer Security (TLS). As you’d expect, this involves generating SSL certificates for both the server and the control utility. Once properly setup to support TLS, you might need to explicitly tell unbound-control
to use TLS with the -s
option if it's not automatically detected.
- Run the
unbound-control-setup
utility to generate the necessary certificates and keys.
sudo unbound-control-setup
- Within the
remote-control:
block of yourunbound.conf
file, ensure thecontrol-enable:
option is set toyes
and then add the remaining TLS related directives that point to the relevant keys and certs.
server:
# ... other server configurations ...
remote-control:
control-enable: yes
control-interface: 127.0.0.1 # Or a specific IP address
control-port: 8953 # Default control port
server-key-file: "/etc/unbound/unbound_server.key"
server-cert-file: "/etc/unbound/unbound_server.pem"
control-key-file: "/etc/unbound/unbound_control.key"
control-cert-file: "/etc/unbound/unbound_control.pem"
Wrapping Up
Unbound makes DNS resolution fast, secure, and simple on Rocky Linux from CIQ (RLC) and other Enterprise Linux distros. Whether you're serving DNS in a small office, powering a home lab, or supporting HPC clusters, Unbound is the lightweight powerhouse you can trust.
Its straightforward configuration, robust DNS-over-TLS support, and built-in caching capabilities mean less fuss and more reliability—exactly what you’d expect from a great DNS server.
Built for Scale. Chosen by the World’s Best.
1.4M+
Rocky Linux instances
Being used world wide
90%
Of fortune 100 companies
Use CIQ supported technologies
250k
Avg. monthly downloads
Rocky Linux