Percio Castelo.
PT Home
How to enable new servers in a HestiaCP DNS cluster
← Back to blog

How to enable new servers in a HestiaCP DNS cluster

6/14/2026 · 6 min · Infrastructure · 711 views

Managing highly available DNS clusters is a critical requirement in resilient hosting infrastructures. Within the HestiaCP ecosystem, each new server provisioned with the BIND9 DNS daemon can act as either a primary (Master) or secondary (Slave) node. Updates made on a master node must be replicated quickly and securely to secondary nameservers (ns01 and ns02) to maintain data integrity and ensure rapid resolution responses to public DNS queries.

In this detailed technical guide, we document the step-by-step procedure to integrate and authorize new servers in your HestiaCP-managed DNS cluster. This covers panel-level API whitelisting, key security adjustments in the BIND daemon configurations (named.conf.options), network security rules, and the CLI utilities required to register, synchronize, and test zone propagation.


1. Topology and cluster data path#

Each new server added to the HestiaCP cluster acts as the primary authority (Master) for the DNS zones created under its accounts. All initial zone records are updated locally on this server. HestiaCP then handles zone transfers via the AXFR protocol (dig @server AXFR) using its internal REST API to trigger asynchronous synchronization to the secondary nameservers (ns01 and ns02).

This real-time replication flow operates under the following data paths:

  1. Replication triggered by external integrations (e.g., API / WHMCS):
   WHMCS -> API -> Master Server -> NS01/NS02 (real-time sync)
  1. Replication executed directly from the HestiaCP web interface:
   Master Server -> NS01/NS02 (real-time sync)

Zone propagation updates are applied only on the configured secondary nameservers, keeping the records synchronized across the public authoritative DNS servers.


2. Cluster node prerequisites and security checks#

Before enabling a new server in the HestiaCP DNS cluster, verify that you have gathered the following networking details and credentials:

  • Internal IP Addresses: The local/internal IP mapping of the servers (essential so that API and BIND socket traffic are restricted to private RFC 1918 networks).
  • API User: For cluster replication, HestiaCP utilizes the default system administrator account (admin) or custom restricted API keys.
  • HTTPS API Communication: Ensure that the API communication occurs strictly over secure HTTPS connections (Port 8083). If your secondary server does not have a valid SSL certificate for the panel hostname, configure it beforehand:
  v-add-letsencrypt-host admin your-panel-hostname
  • dns-cluster User Audit on Slave: HestiaCP stores and isolates the replicated zones under the special dns-cluster system user on the secondary servers. Before registering the remote host, check if this user exists on the Slave server (ns01/ns02):
  # Check if the user exists on the secondary server
  sudo /usr/local/hestia/bin/v-list-user dns-cluster
  
  # If it doesn't exist, create it
  sudo /usr/local/hestia/bin/v-add-user dns-cluster 'YOUR_SECURE_PASSWORD'
  sudo /usr/local/hestia/bin/v-add-user-dns-cluster dns-cluster

3. Authorizing HestiaCP API communication#

To prevent API connection refusals, the IP addresses of the secondary servers (ns01 and ns02) must be whitelisted in the API configuration of the new Master server, and vice versa.

Step 1: Whitelisting the master server on the secondary nodes (ns01/ns02)#

  1. Log in to the HestiaCP panel on port 8083 of each secondary DNS server using the admin account via HTTPS.
  2. In the top-right menu, click Server Settings (gear icon) and select Configure.
  3. Select the Security tab -> System -> API.
  4. Locate the Allowed IP addresses for API field.
  5. Add a new line containing the internal IP address of the new Master server.
  6. Click Save.

Step 2: Whitelisting the secondary nodes on the master server#

On the panel of the new Master server being added to the cluster, repeat the process to authorize API calls originating from the secondary nodes.

  1. Access the HestiaCP panel of the new Master server (https://your-vps:8083).
  2. Navigate to Server Settings >> Configure >> Security >> System >> API.
  3. Under the Allowed IP addresses for API field, enter the authorized IP addresses. Ensure you whitelist the loopback interface and the internal IPs of both secondary nodes:
  • 127.0.0.1 (Local loopback)
  • 172.16.45.5 (NS01 internal IP)
  • 172.16.45.6 (NS02 internal IP)
  1. Click Save to apply the changes.

4. Securing and tuning BIND9 (named.conf.options / named.conf)#

To allow secure zone transfers (AXFR) from the new Master to the secondary nodes (ns01 and ns02), the configuration options file for the BIND daemon must be adjusted.

Step 0: Mandatory backup#

Before modifying the BIND9 configuration, create backup copies of your configuration files for rapid recovery:

Debian/Ubuntu:

# Backup named.conf.options with a timestamp suffix
sudo cp /etc/bind/named.conf.options /etc/bind/named.conf.options.backup.$(date +%Y%m%d-%H%M%S)

# Full backup of the BIND configuration directory
sudo tar czf /root/bind-backup-$(date +%Y%m%d).tar.gz /etc/bind/

RHEL/Rocky Linux/AlmaLinux:

# Backup named.conf with a timestamp suffix
sudo cp /etc/named.conf /etc/named.conf.backup.$(date +%Y%m%d-%H%M%S)

Step 1: Modifying the configuration#

Open the appropriate file on the Master server in your preferred text editor:

Debian/Ubuntu:

sudo nano /etc/bind/named.conf.options

RHEL/Rocky Linux/AlmaLinux:

sudo nano /etc/named.conf

Modify or configure the options block to match the following structure:

options {
        directory "/var/cache/bind"; // On RHEL/Rocky use "/var/named"
        
        dnssec-validation auto;
        auth-nxdomain no;

        // Limits DNS recursion exclusively to the localhost interface
        allow-recursion { 127.0.0.1; };

        // Restricts zone transfers (AXFR) exclusively to NS01 and NS02
        allow-transfer { 172.16.45.5; 172.16.45.6; };

        // Notifies secondary (NS) nodes of local zone updates
        allow-notify { 172.16.45.5; 172.16.45.6; };
        also-notify { 172.16.45.5; 172.16.45.6; };

        hostname none;
        server-id none;
        version none;
};

Step 2: Syntax validation and service restart#

Save your changes, then validate the configuration files syntax using the named-checkconf tool before reloading the service:

# Validate on Debian/Ubuntu (if the command runs silently, the syntax is correct)
sudo named-checkconf /etc/bind/named.conf.options

# Validate on RHEL/Rocky Linux/AlmaLinux
sudo named-checkconf /etc/named.conf

If a syntax error occurs, the terminal will return the line number and error details. Correct the file before proceeding.

If the validation completes successfully, restart the appropriate DNS service:

# Restart on Debian/Ubuntu
sudo systemctl restart bind9

# Restart on RHEL/Rocky Linux/AlmaLinux
sudo systemctl restart named

Emergency rollback procedure#

If the DNS service fails to restart or stops responding:

Debian/Ubuntu:

# 1. Restore the configuration backup
sudo cp /etc/bind/named.conf.options.backup.* /etc/bind/named.conf.options

# 2. Validate syntax and restart
sudo named-checkconf /etc/bind/named.conf.options
sudo systemctl restart bind9

RHEL/Rocky Linux/AlmaLinux:

# 1. Restore the configuration backup
sudo cp /etc/named.conf.backup.* /etc/named.conf

# 2. Validate syntax and restart
sudo named-checkconf /etc/named.conf
sudo systemctl restart named

5. Firewall configuration for zone transfers (AXFR)#

Standard DNS resolution queries use UDP port 53. However, zone replication and zone transfers (AXFR) between primary and secondary nameservers use TCP port 53. You must whitelist incoming TCP port 53 traffic specifically between the IP addresses of your cluster nodes.

Configuration via UFW (debian/ubuntu)#

# Allow incoming TCP/UDP connections on port 53 for cluster IPs
sudo ufw allow from 172.16.45.5 to any port 53 proto tcp
sudo ufw allow from 172.16.45.6 to any port 53 proto tcp
sudo ufw reload

Configuration via raw iptables#

sudo iptables -A INPUT -p tcp -s 172.16.45.5 --dport 53 -j ACCEPT
sudo iptables -A INPUT -p tcp -s 172.16.45.6 --dport 53 -j ACCEPT
# Persist the ruleset
sudo netfilter-persistent save || sudo sh -c 'iptables-save > /etc/iptables/rules.v4'

6. Registering and synchronizing remote hosts in HestiaCP#

With the API whitelists and BIND daemon options configured, register the secondary nodes on the HestiaCP Master server.

Safe node registration via environment variables or API keys#

I recommend generating a dedicated API Key inside the panel at Server Settings -> API -> Generate for isolated access credentials.

If using the remote admin password, register the nodes using temporary environment variables wrapped by shell directives that temporarily disable command history logging:

# 1. Temporarily disable command history logging in the terminal
set +o history

# 2. Export the password to the local shell environment
export NS_PASS="YOUR_NS01_PASSWORD"

# 3. Register NS01 under the dedicated dns-cluster system account
sudo /usr/local/hestia/bin/v-add-remote-dns-host 172.16.45.5 8083 'admin' "$NS_PASS" 'api' 'dns-cluster'

# 4. Register NS02
export NS_PASS="YOUR_NS02_PASSWORD"
sudo /usr/local/hestia/bin/v-add-remote-dns-host 172.16.45.6 8083 'admin' "$NS_PASS" 'api' 'dns-cluster'

# 5. Clean up environment variables and re-enable history logging
unset NS_PASS
set -o history

The added hosts are recorded in the cluster configuration file located at dns-cluster.conf.

Manual immediate synchronization#

HestiaCP runs background replication tasks every 1-5 minutes via cron. To trigger manual zone synchronization and test connections immediately, run:

sudo /usr/local/hestia/bin/v-sync-dns-cluster

To verify the zone replication was successful, access the shell on either secondary nameserver and list the imported zones under the dns-cluster account:

sudo /usr/local/hestia/bin/v-list-dns-domains dns-cluster

7. Diagnostics and troubleshooting#

System log auditing#

If synchronization errors occur, audit the detailed log outputs:

  • BIND9 syslog entries: sudo tail -f /var/log/syslog | grep named (on RHEL, check messages)
  • HestiaCP system logs: sudo tail -f /usr/local/hestia/log/system.log (check system.log) and /usr/local/hestia/log/history.log (check history.log)
  • Cluster replication issues: sudo grep "dns-cluster" /var/log/syslog (or messages on RHEL)

Query diagnostics using dig#

To verify that the DNS zones have propagated and resolve authoritatively from outside the private network, use query diagnostic tools like dig.

A healthy and synchronized cluster requires the SOA (Start of Authority) serial numbers returned on the Master and Slaves to be exactly identical:

# Querying the SOA record on NS01
dig SOA +short @ns01.domain.com dev.domain.com
# Output: ns01.domain.com. root.dev.domain.com. 2024091201 7200 3600 1209600 180

# Querying the SOA record on NS02 (must match the master/ns01 serial)
dig SOA +short @ns02.domain.com dev.domain.com
# Output: ns01.domain.com. root.dev.domain.com. 2024091201 7200 3600 1209600 180

If the serial numbers returned diverge (e.g., 2024091201 on NS01 and 2024091202 on NS02), the zones are out of sync. Force manual synchronization or check the BIND9 syslog entries for zone transfer errors.


8. DNS cluster integrity validation script#

The script below checks connection pings, validates HestiaCP API responses over HTTPS, audits the BIND9 daemon active status, and compares zone SOA serial numbers across servers to ensure cluster integrity:

#!/bin/bash
# validate_dns_cluster.sh - Validate HestiaCP DNS Cluster Integrity
# ⚠️ Run this script with appropriate privileges

set -e

# Configure infrastructure IP addresses
MASTER_IP="172.16.45.4"
SLAVES=("172.16.45.5" "172.16.45.6")
DOMAIN="dev.domain.com"

# Terminal output formatting colors
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'

log() { echo -e "${GREEN}[OK]${NC} $1"; }
error() { echo -e "${RED}[FAIL]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }

echo "=== Testing Network Connectivity (Ping) ==="
for slave in "${SLAVES[@]}"; do
    if ping -c 1 -W 2 "$slave" &>/dev/null; then
        log "Slave $slave responding to ICMP packets"
    else
        error "Slave $slave unreachable via network"
    fi
done

echo ""
echo "=== Testing HestiaCP API via HTTPS (Port 8083) ==="
for slave in "${SLAVES[@]}"; do
    HTTP_CODE=$(curl -sk -o /dev/null -w "%{http_code}" "https://$slave:8083/")
    if [[ "$HTTP_CODE" == "200" || "$HTTP_CODE" == "401" ]]; then
        log "API on $slave active (HTTP $HTTP_CODE)"
    else
        error "API on $slave failed (HTTP $HTTP_CODE)"
    fi
done

echo ""
echo "=== Checking SOA Serial Synchronization ==="
MASTER_SERIAL=$(dig SOA +short @${MASTER_IP} "$DOMAIN" | awk '{print $3}')
if [[ -z "$MASTER_SERIAL" ]]; then
    error "Could not retrieve SOA serial from Master (${MASTER_IP})"
else
    log "Serial on Master: $MASTER_SERIAL"
    for slave in "${SLAVES[@]}"; do
        SLAVE_SERIAL=$(dig SOA +short @"$slave" "$DOMAIN" | awk '{print $3}')
        if [[ "$MASTER_SERIAL" == "$SLAVE_SERIAL" ]]; then
            log "Slave $slave successfully synchronized ($SLAVE_SERIAL)"
        else
            warn "Slave $slave is out of sync! Master: $MASTER_SERIAL, Slave: $SLAVE_SERIAL"
        fi
    done
fi

echo ""
echo "=== Audit Completed ==="

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

711 reads
Creative Commons Attribution-NonCommercial

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments