If you want to reach machines on your internal network by domain name, or need to resolve IPs back into domain names, you can install BIND on Rocky Linux 9 and run your own DNS server. This post walks through the complete configuration process for forward resolution (domain to IP) and reverse resolution (IP to domain): installation, the main configuration, zone files, syntax checks, startup, and firewall — every step comes with a verification point. All commands were actually run inside a rockylinux:9 container (BIND 9.16.23-RH). Since the container has no systemd, the service was started with named -u named and verified; the corresponding systemctl commands for physical machines and VMs are listed alongside. A CentOS 7 differences table is included at the end.

What you end up with is an authoritative DNS server: www.example.com resolves to 192.168.254.150, and looking up 192.168.254.150 returns the domain name.

Prerequisites

  • OS: Rocky Linux 9 (tested in a rockylinux:9 container, BIND 9.16.23-RH, dnf 4.14.0, aarch64)
  • Privileges: root
  • Ports: DNS uses UDP/TCP 53, so the firewall must allow it
  • Concepts: a forward zone maps domain names to IPs (A records), a reverse zone maps IPs back to domain names (PTR records). They are two independent zone files, declared separately in named.conf

Installing BIND

On Rocky Linux 9, software is installed with dnf. One command gets you the BIND daemon plus the companion tools (diagnostic commands like nslookup and dig live in bind-utils):

1
dnf install -y bind bind-utils

Verification point: named -v prints the version number. In my test the installed version was 9.16.23-RH (the Red Hat extended-support build):

Figure 1

One side note: on Rocky 9, /usr/bin/yum is a symlink to dnf, so yum install and dnf install are literally the same command. Prefer writing dnf on new systems; there’s no rush to rewrite yum in old scripts.

Configuring BIND

Editing the Main Configuration File

Open BIND’s main configuration file /etc/named.conf in a text editor:

1
vim /etc/named.conf

The options block controls listening and query permissions. The default configuration listens only on 127.0.0.1 and only allows local queries; to serve other machines, change it to any (or specify your internal subnets):

1
2
3
4
5
6
7
8
options {
listen-on port 53 { any; };
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
allow-query { any; };
};

Declaring the Forward and Reverse Zones

Declare two zones in /etc/named.conf. The forward zone corresponds to example.com; the reverse zone is the subnet written backwards plus the in-addr.arpa suffix (192.168.254.x reversed becomes 254.168.192):

1
2
3
4
5
6
7
8
9
10
11
zone "example.com" IN {
type master;
file "example.com.zone";
allow-update { none; };
};

zone "254.168.192.in-addr.arpa" IN {
type master;
file "192.168.254.zone";
allow-update { none; };
};

Creating the Zone Files

Change into /var/named and create the forward zone file first:

1
vim /var/named/example.com.zone

The contents are below; the A record points the domain to 192.168.254.150. Note that the trailing dot on domain names in the SOA line cannot be omitted — a trailing dot means an absolute (fully qualified) name, while omitting it gets the zone name appended automatically, producing ns1.example.com.example.com:

1
2
3
4
5
6
7
8
9
10
11
$TTL 86400
@ IN SOA ns1.example.com. admin.example.com. (
2023101001 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ) ; Minimum TTL
;
@ IN NS ns1.example.com.
ns1 IN A 192.168.254.150
www IN A 192.168.254.150

Then create the reverse zone file:

1
vim /var/named/192.168.254.zone

The contents are below; the PTR records map the IP back to domain names. Record names only carry the host portion (150), not the full IP:

1
2
3
4
5
6
7
8
9
10
11
$TTL 86400
@ IN SOA ns1.example.com. admin.example.com. (
2023101001 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ) ; Minimum TTL
;
@ IN NS ns1.example.com.
150 IN PTR ns1.example.com.
150 IN PTR www.example.com.

Before Starting: Three Syntax Checks

One nice thing about BIND is that you don’t have to wait for runtime to discover configuration mistakes — three checking tools can catch every problem before startup. named-checkconf validates the main configuration, named-checkzone validates zone files:

1
2
3
named-checkconf /etc/named.conf
named-checkzone example.com /var/named/example.com.zone
named-checkzone 254.168.192.in-addr.arpa /var/named/192.168.254.zone

Verification point: checkconf exits silently with code 0; checkzone prints loaded serial 2023101001 followed by OK. All three checks passed in my test:

Figure 2

Change the group and owner of the zone files, otherwise the named service (which runs as the named user) can’t read them:

1
2
chown root:named /var/named/example.com.zone
chown root:named /var/named/192.168.254.zone

Starting the Service

On a physical machine or VM, manage the service with systemd; start it for the first time and enable it at boot:

1
systemctl enable named --now

Verification point: systemctl status named shows active (running).

Containers have no systemd, so in this post the daemon was started directly as the named user and verified:

1
named -u named

Verification point: /run/named/named.pid appears and the process is alive; dig returning records (next section) is the real proof.

Firewall Settings

Allow DNS traffic (port 53). firewalld depends on systemd, so it can’t be installed or started inside a container — the two commands below are the standard approach on physical machines and VMs (not run in the container):

1
2
firewall-cmd --permanent --add-service=dns
firewall-cmd --reload

Verification point: firewall-cmd --list-services output includes dns.

Testing Resolution

Testing Forward Resolution

dig is the most direct verification tool; @127.0.0.1 targets this machine’s DNS server:

1
dig @127.0.0.1 www.example.com A +short

Testing Reverse Resolution

The -x flag makes dig automatically do the IP-to-domain lookup:

1
dig @127.0.0.1 -x 192.168.254.150 +short

Verification point: forward lookup returns 192.168.254.150; reverse lookup returns the two domain names in the PTR records. nslookup’s more graphical output was also tested:

Figure 3

Historical Version Differences (CentOS 7)

When migrating from or comparing against older CentOS 7 environments, watch for these points:

  • System lifecycle: CentOS 7 reached EOL in June 2024 and the official mirrors are gone. When yum returns 403 or No package bind available, comment out mirrorlist in the repo files and point baseurl at http://vault.centos.org before installing.
  • Package manager commands: el7 uses yum, el9 uses dnf. Package names are identical (bind, bind-utils), and paths and configuration file locations are exactly the same too, so configurations port over unchanged.
  • BIND version: el7 ships 9.11.4-P2, while Rocky 9 tested at 9.16.23-RH. All the syntax used in this post works on both versions.

Common Errors

  • named won’t start, and the journal reports loading configuration: permission denied: the zone file ownership is wrong. Run chown root:named and restart.
  • Service is healthy but resolution times out: the firewall isn’t allowing port 53, or allow-query in options is still the default localhost.
  • Wrong resolution results (NXDOMAIN): a missing trailing dot on a domain name in the zone file (getting the zone name appended twice), or the Serial wasn’t bumped so stale data came from cache/secondary DNS — increment the Serial after every zone file change.
  • Reverse resolution doesn’t work: the reverse zone name is wrong. The reverse zone for 192.168.254.x must be 254.168.192.in-addr.arpa (the first three octets reversed), and PTR record names carry only the last octet.

Notes and Cautions

  • Run named-checkconf and named-checkzone to validate syntax after every configuration change, before starting or restarting the service — this eliminates most of the “service won’t start” troubleshooting time.
  • Remember to chown root:named on zone files. With wrong permissions, named can’t read the files and resolution silently fails.
  • Reverse zone names are the subnet reversed: 192.168.254.x corresponds to 254.168.192.in-addr.arpa, and PTR record names are just the host portion.
  • allow-query { any; } allows queries from any source. If the server sits on the public internet, narrow it to the subnets that actually need access — an open-recursive DNS server will get recruited for amplification attacks.
  • Troubleshooting logs are in /var/log/messages (tail -f /var/log/messages and filter for named); for client-side debugging, dig +trace shows the full resolution chain.
  • Rollback plan: systemctl disable --now named takes the service offline; if a configuration change breaks things, restore your pre-change backup or the dist defaults (rpm -V bind shows which files were modified).

From package installation to a correct dig result, the whole process takes under twenty minutes. BIND has many configuration files, but the three syntax-checking tools eliminate the “misconfigured, only found out at runtime” problem — build the habit of running checkconf/checkzone before starting, and this DNS server will basically never give you trouble.

This article was rebuilt from the author’s CSDN blog posts published between 2020 and 2024, originally published on CSDN.