How to Set Up a DNS Server with BIND9 on RHEL 7

The Berkeley Internet Name Domain (BIND) is the most widely deployed DNS server software in the world, and BIND 9 is the version available in the RHEL 7 package repositories. Setting up your own DNS server gives you authoritative control over a domain zone, enables split-horizon DNS for internal network resolution, and removes dependency on external resolvers for private hostnames. On Red Hat Enterprise Linux 7, the BIND package is called bind, the companion utilities are in bind-utils, and the service is managed through systemctl under the unit name named. This guide covers a complete, working DNS server installation including a forward lookup zone and a reverse lookup zone, tested with dig and nslookup.

Prerequisites

  • RHEL 7 system with root or sudo privileges.
  • A static IP address already configured on the server (see the NetworkManager guide for setup steps).
  • A registered domain name or a private domain you control for internal use (this guide uses example.lan with server IP 192.168.1.10).
  • Firewall access to port 53 TCP/UDP.
  • Active RHEL subscription or a configured YUM repository containing the bind package.

Step 1: Install BIND and Utilities

Install the BIND server and the companion diagnostic tools in a single yum command.

yum install -y bind bind-utils

The bind package installs the named daemon along with its default configuration skeleton. The bind-utils package provides dig, nslookup, and host — essential tools for testing your server after configuration. Verify the installed version:

named -v

Step 2: Configure /etc/named.conf

The primary configuration file for BIND is /etc/named.conf. Open it with your preferred editor and modify the options block to make the server listen on the correct interfaces and restrict queries appropriately.

vim /etc/named.conf

Replace or adjust the options block to match the following:

options {
    listen-on port 53 { 127.0.0.1; 192.168.1.10; };
    listen-on-v6 port 53 { ::1; };
    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     { localhost; 192.168.1.0/24; };
    allow-recursion { localhost; 192.168.1.0/24; };

    forwarders {
        8.8.8.8;
        8.8.4.4;
    };
    forward only;

    recursion yes;
    dnssec-enable yes;
    dnssec-validation yes;
    dnssec-lookaside auto;

    bindkeys-file "/etc/named.iscdlv.key";
    managed-keys-directory "/var/named/dynamic";
    pid-file "/run/named/named.pid";
    session-keyfile "/run/named/session.key";
};

Key directives explained: listen-on restricts the interfaces BIND binds to — including both loopback and the server’s static IP; allow-query restricts which clients may send queries to the server; allow-recursion prevents your server from becoming an open recursive resolver; forwarders with forward only sends any queries not answered by a local zone upstream to Google’s public DNS.

Step 3: Add Zone Declarations to named.conf

At the bottom of /etc/named.conf, before the closing brace, add zone stanzas for your forward and reverse zones.

zone "example.lan" IN {
    type master;
    file "example.lan.zone";
    allow-update { none; };
};

zone "1.168.192.in-addr.arpa" IN {
    type master;
    file "192.168.1.rev";
    allow-update { none; };
};

The type master directive makes this server authoritative for the zone. The file value is relative to the directory option defined in the options block (/var/named). allow-update { none; } disables dynamic DNS updates, which is correct for a statically managed zone.

Step 4: Create the Forward Zone File

Create the zone file for forward lookups (hostname to IP).

vim /var/named/example.lan.zone
$TTL 86400
@   IN  SOA     ns1.example.lan. admin.example.lan. (
                2026051701  ; Serial (YYYYMMDDnn)
                3600        ; Refresh
                1800        ; Retry
                604800      ; Expire
                86400 )     ; Negative Cache TTL

; Name servers
@   IN  NS      ns1.example.lan.

; A records
ns1     IN  A   192.168.1.10
server1 IN  A   192.168.1.20
server2 IN  A   192.168.1.21
www     IN  A   192.168.1.20
mail    IN  A   192.168.1.22

; MX record
@   IN  MX  10  mail.example.lan.

The SOA (Start of Authority) record is mandatory. The serial number must be incremented every time you modify the zone file; the convention YYYYMMDDnn helps track dates. The @ symbol represents the zone origin (example.lan). Every hostname in the file that does not end with a trailing dot is relative to the zone origin.

Step 5: Create the Reverse Zone File

vim /var/named/192.168.1.rev
$TTL 86400
@   IN  SOA     ns1.example.lan. admin.example.lan. (
                2026051701  ; Serial
                3600        ; Refresh
                1800        ; Retry
                604800      ; Expire
                86400 )     ; Negative Cache TTL

@   IN  NS      ns1.example.lan.

10  IN  PTR     ns1.example.lan.
20  IN  PTR     server1.example.lan.
21  IN  PTR     server2.example.lan.
22  IN  PTR     mail.example.lan.

Reverse zone PTR records map IP addresses back to hostnames. The numbers on the left are the last octet of each IP address. The zone is named 1.168.192.in-addr.arpa in named.conf, which covers the entire 192.168.1.0/24 subnet.

Step 6: Set Correct Ownership on Zone Files

chown named:named /var/named/example.lan.zone
chown named:named /var/named/192.168.1.rev

Step 7: Validate the Configuration

Always validate configuration and zone files before starting the service to catch syntax errors.

# Check the main named.conf syntax
named-checkconf /etc/named.conf

# Check the forward zone file
named-checkzone example.lan /var/named/example.lan.zone

# Check the reverse zone file
named-checkzone 1.168.192.in-addr.arpa /var/named/192.168.1.rev

Both named-checkzone commands should print OK if the files are correct. If any errors are reported, review the indicated line numbers and correct them before proceeding.

Step 8: Start and Enable the named Service

systemctl start named
systemctl enable named
systemctl status named

The enable command creates the appropriate symlink to start named automatically at boot. Verify the service is active (running) in the status output.

Step 9: Open the Firewall

firewall-cmd --permanent --add-service=dns
firewall-cmd --reload
firewall-cmd --list-services

The dns service definition in firewalld opens both TCP and UDP port 53, which are both required for DNS operation (UDP for standard queries, TCP for zone transfers and large responses).

Step 10: Test with dig and nslookup

# Forward lookup
dig @192.168.1.10 ns1.example.lan
dig @192.168.1.10 www.example.lan
dig @192.168.1.10 mail.example.lan MX

# Reverse lookup
dig @192.168.1.10 -x 192.168.1.20

# Using nslookup interactively
nslookup
> server 192.168.1.10
> www.example.lan
> 192.168.1.22
> exit

# Test recursion (external query forwarded upstream)
dig @192.168.1.10 google.com

A successful dig response will include an ANSWER SECTION with the correct IP address and a status of NOERROR. The AUTHORITY SECTION will reference ns1.example.lan as the authoritative nameserver, confirming that your server owns the zone.

Running a BIND DNS server on RHEL 7 provides reliable, low-latency name resolution for your internal network and gives administrators full control over zone data. The combination of named-checkconf and named-checkzone validation, firewalld service management, and systemctl lifecycle control makes BIND on RHEL 7 a robust and manageable solution. Once your primary nameserver is operational, you can extend this setup with a secondary slave server for redundancy by adding type slave zones that transfer from the master — a natural next step for any production environment.