Blog
Как настроить HestiaCP с двумя отдельными DNS-серверами: master + slave DNS
В этой статье разберём схему установки HestiaCP на три сервера:
host.domain.ru — основной сервер HestiaCP, хостинг, сайты, базы, панель управления
ns1.domain.ru — первый DNS-only сервер
ns2.domain.ru — второй DNS-only сервер
Идея простая: все сайты, пользователи и DNS-зоны создаются на основном сервере host.domain.ru, а два отдельных сервера ns1.domain.ru и ns2.domain.ru автоматически получают DNS-зоны с master-сервера и отвечают за домены клиентов в интернете.
Такой вариант удобен для хостинга: клиенту достаточно указать у регистратора домена только:
ns1.domain.ru
ns2.domain.ru
А управление зонами остаётся в HestiaCP на основном сервере.
1. Общая схема
Будем использовать такие условные данные:
Домен: domain.ru
Master: host.domain.ru
IP master: IP_HOST
Slave DNS 1: ns1.domain.ru
IP ns1: IP_NS1
Slave DNS 2: ns2.domain.ru
IP ns2: IP_NS2
В реальной установке замените:
domain.ru → ваш домен
IP_HOST → IP основного сервера
IP_NS1 → IP первого DNS-сервера
IP_NS2 → IP второго DNS-сервера
2. Что нужно подготовить заранее
Нужно три чистых VPS/VDS-сервера. Желательно использовать поддерживаемую версию Debian или Ubuntu. Для продакшена лучше брать стабильную и официально поддерживаемую версию ОС. Если вы хотите ставить на Debian 13, сначала проверьте, поддерживает ли его текущая версия HestiaCP. Если нет — безопаснее использовать Debian 12.
На всех трёх серверах нужен root-доступ.
Перед установкой желательно сразу назначить hostname:
host.domain.ru
ns1.domain.ru
ns2.domain.ru
3. Настройка у регистратора домена
У регистратора домена domain.ru нужно сделать две вещи.
Первая — создать child nameservers, они же glue records:
ns1.domain.ru → IP_NS1
ns2.domain.ru → IP_NS2
Например:
ns1.domain.ru → 111.111.111.111
ns2.domain.ru → 222.222.222.222
Вторая — делегировать сам домен domain.ru на эти DNS-серверы:
domain.ru NS ns1.domain.ru
domain.ru NS ns2.domain.ru
Это важный момент. Glue records только связывают имена ns1.domain.ru и ns2.domain.ru с IP-адресами. Но сам домен ещё должен быть делегирован на эти NS.
4. Подготовка серверов
На каждом сервере обновляем систему.
На master-сервере:
hostnamectl set-hostname host.domain.ru
apt update
apt upgrade -y
apt install -y curl wget ca-certificates gnupg lsb-release nano dnsutils
reboot
На первом DNS-сервере:
hostnamectl set-hostname ns1.domain.ru
apt update
apt upgrade -y
apt install -y curl wget ca-certificates gnupg lsb-release nano dnsutils
reboot
На втором DNS-сервере:
hostnamectl set-hostname ns2.domain.ru
apt update
apt upgrade -y
apt install -y curl wget ca-certificates gnupg lsb-release nano dnsutils
reboot
5. Установка HestiaCP на master-сервер
На сервере host.domain.ru ставим полноценную HestiaCP-панель.
Если почта на сервере не нужна, можно ставить без Exim и Dovecot:
cd /root
wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh
bash hst-install.sh \
--interactive no \
--hostname host.domain.ru \
--email admin@domain.ru \
--password 'СЛОЖНЫЙ_ПАРОЛЬ' \
--lang ru \
--apache yes \
--phpfpm yes \
--multiphp yes \
--vsftpd yes \
--named yes \
--mysql yes \
--postgresql no \
--exim no \
--dovecot no \
--sieve no \
--clamav no \
--spamassassin no \
--iptables yes \
--fail2ban yes \
--quota yes \
--api yes \
--port 8083
Если нужна почта, тогда вместо блока:
--exim no
--dovecot no
--sieve no
используйте:
--exim yes \
--dovecot yes \
--sieve yes \
--spamassassin yes \
После установки панель будет доступна по адресу:
https://host.domain.ru:8083
6. Установка HestiaCP на DNS-only серверы
На ns1.domain.ru и ns2.domain.ru не нужны Apache, PHP, MySQL, почта и FTP. Эти серверы будут использоваться только для DNS.
На ns1.domain.ru:
cd /root
wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh
bash hst-install.sh \
--interactive no \
--hostname ns1.domain.ru \
--email admin@domain.ru \
--password 'СЛОЖНЫЙ_ПАРОЛЬ' \
--lang ru \
--apache no \
--phpfpm no \
--multiphp no \
--vsftpd no \
--proftpd no \
--named yes \
--mysql no \
--postgresql no \
--exim no \
--dovecot no \
--sieve no \
--clamav no \
--spamassassin no \
--iptables yes \
--fail2ban yes \
--quota no \
--api yes \
--port 8083
На ns2.domain.ru команда аналогичная:
cd /root
wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh
bash hst-install.sh \
--interactive no \
--hostname ns2.domain.ru \
--email admin@domain.ru \
--password 'СЛОЖНЫЙ_ПАРОЛЬ' \
--lang ru \
--apache no \
--phpfpm no \
--multiphp no \
--vsftpd no \
--proftpd no \
--named yes \
--mysql no \
--postgresql no \
--exim no \
--dovecot no \
--sieve no \
--clamav no \
--spamassassin no \
--iptables yes \
--fail2ban yes \
--quota no \
--api yes \
--port 8083
7. Включение API на DNS-серверах
Master-сервер должен подключаться к ns1 и ns2 по API HestiaCP. Поэтому на обоих DNS-серверах нужно включить API и разрешить IP master-сервера.
На ns1.domain.ru:
IP_HOST="IP_HOST"
/usr/local/hestia/bin/v-change-sys-api enable api
/usr/local/hestia/bin/v-add-sys-api-ip "$IP_HOST"
systemctl restart hestia
На ns2.domain.ru:
IP_HOST="IP_HOST"
/usr/local/hestia/bin/v-change-sys-api enable api
/usr/local/hestia/bin/v-add-sys-api-ip "$IP_HOST"
systemctl restart hestia
Также желательно открыть порт 8083 только для IP master-сервера и для вашего рабочего IP.
Пример для Hestia firewall:
/usr/local/hestia/bin/v-add-firewall-rule ACCEPT IP_HOST 8083 TCP "Allow Hestia API from master"
8. Включение режима hestia-zone
Для схемы master/slave в HestiaCP нужно использовать режим:
DNS_CLUSTER_SYSTEM='hestia-zone'
На всех трёх серверах выполните:
sed -i "s/^DNS_CLUSTER_SYSTEM=.*/DNS_CLUSTER_SYSTEM='hestia-zone'/" /usr/local/hestia/conf/hestia.conf
grep -q "^DNS_CLUSTER_SYSTEM=" /usr/local/hestia/conf/hestia.conf || \
echo "DNS_CLUSTER_SYSTEM='hestia-zone'" >> /usr/local/hestia/conf/hestia.conf
systemctl restart hestia
9. Настройка BIND на master-сервере
На master-сервере host.domain.ru нужно разрешить передачу DNS-зон на slave-серверы.
Откройте файл:
nano /etc/bind/named.conf.options
Внутри блока options { ... } добавьте или измените параметры:
allow-transfer { IP_NS1; IP_NS2; };
also-notify { IP_NS1; IP_NS2; };
Пример:
options {
directory "/var/cache/bind";
dnssec-validation auto;
auth-nxdomain no;
allow-transfer { IP_NS1; IP_NS2; };
also-notify { IP_NS1; IP_NS2; };
hostname none;
server-id none;
version none;
};
Проверяем конфигурацию и перезапускаем BIND:
named-checkconf
systemctl restart named || systemctl restart bind9
10. Настройка BIND на DNS-slave серверах
На ns1.domain.ru и ns2.domain.ru нужно разрешить уведомления от master-сервера.
Откройте файл:
nano /etc/bind/named.conf.options
Пример конфига для DNS-only slave:
options {
directory "/var/cache/bind";
dnssec-validation auto;
auth-nxdomain no;
listen-on port 53 { any; };
listen-on-v6 { none; };
allow-query { any; };
recursion no;
allow-recursion { none; };
allow-notify { IP_HOST; };
allow-transfer { "none"; };
hostname none;
server-id none;
version none;
};
После изменения:
named-checkconf
named-checkconf -z
systemctl restart named || systemctl restart bind9
Важно: для публичного авторитетного DNS-сервера рекурсию лучше отключать:
recursion no;
allow-recursion { none; };
Иначе сервер может стать открытым рекурсивным DNS-резолвером, что плохо с точки зрения безопасности.
11. Создание DNS-пользователей на slave-серверах
На ns1.domain.ru создаём отдельного пользователя, под которым будут храниться DNS-зоны:
DNS_PASS_NS1='СЛОЖНЫЙ_ПАРОЛЬ'
/usr/local/hestia/bin/v-add-user dns-ns1 "$DNS_PASS_NS1" dns-ns1@domain.ru
/usr/local/hestia/bin/v-change-user-config-value dns-ns1 ROLE dns-cluster
На ns2.domain.ru:
DNS_PASS_NS2='СЛОЖНЫЙ_ПАРОЛЬ'
/usr/local/hestia/bin/v-add-user dns-ns2 "$DNS_PASS_NS2" dns-ns2@domain.ru
/usr/local/hestia/bin/v-change-user-config-value dns-ns2 ROLE dns-cluster
Если команда изменения роли не подходит для вашей версии HestiaCP, можно сделать это через панель: открыть пользователя и назначить роль dns-cluster.
12. Создание API-ключей на DNS-серверах
На ns1.domain.ru и ns2.domain.ru нужно создать access key для подключения master-сервера.
Рекомендуемый способ — через панель HestiaCP:
https://ns1.domain.ru:8083
https://ns2.domain.ru:8083
В панели:
Admin → Access Keys → Add Access Key
Для ключа желательно выдать минимальные права, необходимые для синхронизации DNS-кластера. После создания вы получите пару:
ACCESS_KEY:SECRET_KEY
Сохраните отдельно ключ для ns1 и отдельно ключ для ns2.
Например:
NS1_ACCESS="ACCESS_KEY_NS1:SECRET_KEY_NS1"
NS2_ACCESS="ACCESS_KEY_NS2:SECRET_KEY_NS2"
13. Подключение slave DNS-серверов к master
На master-сервере host.domain.ru добавляем удалённые DNS-хосты:
NS1_ACCESS='ACCESS_KEY_NS1:SECRET_KEY_NS1'
NS2_ACCESS='ACCESS_KEY_NS2:SECRET_KEY_NS2'
/usr/local/hestia/bin/v-add-remote-dns-host ns1.domain.ru 8083 "$NS1_ACCESS" '' 'api' 'dns-ns1'
/usr/local/hestia/bin/v-add-remote-dns-host ns2.domain.ru 8083 "$NS2_ACCESS" '' 'api' 'dns-ns2'
Проверить список подключённых DNS-хостов:
/usr/local/hestia/bin/v-list-remote-dns-hosts
Если при синхронизации появляется ошибка:
Error: ns1.domain.ru connection failed
нужно проверить:
getent hosts ns1.domain.ru
nc -vz ns1.domain.ru 8083
curl -kI https://ns1.domain.ru:8083/
Частые причины:
1. hostname ns1.domain.ru не резолвится с master-сервера;
2. закрыт порт 8083;
3. API на slave-сервере выключен;
4. IP master-сервера не добавлен в API allowlist;
5. неправильно создан access key.
14. Создание DNS-зоны domain.ru
На master-сервере в HestiaCP создайте DNS-зону:
DNS → Add DNS Zone → domain.ru
В зоне должны быть записи:
@ NS ns1.domain.ru.
@ NS ns2.domain.ru.
ns1 A IP_NS1
ns2 A IP_NS2
host A IP_HOST
Также можно добавить запись для панели:
panel A IP_HOST
Если панель будет открываться по host.domain.ru, достаточно записи:
host A IP_HOST
15. Синхронизация DNS-зон
На master-сервере запускаем синхронизацию:
/usr/local/hestia/bin/v-sync-dns-cluster ns1.domain.ru
/usr/local/hestia/bin/v-sync-dns-cluster ns2.domain.ru
На ns1.domain.ru проверяем:
/usr/local/hestia/bin/v-list-dns-domains dns-ns1
На ns2.domain.ru:
/usr/local/hestia/bin/v-list-dns-domains dns-ns2
Если всё хорошо, в списке должна быть зона:
domain.ru
16. Проверка BIND на DNS-серверах
На каждом DNS-сервере проверяем, что BIND запущен:
systemctl status named --no-pager
или:
systemctl status bind9 --no-pager
Проверяем, слушает ли сервер порт 53:
ss -lntup | grep ':53'
Нужно увидеть, что named слушает внешний IP-адрес сервера:
IP_NS1:53
IP_NS2:53
Проверяем локальный ответ на ns1:
dig @127.0.0.1 domain.ru SOA +short
dig @IP_NS1 domain.ru SOA +short
Проверяем локальный ответ на ns2:
dig @127.0.0.1 domain.ru SOA +short
dig @IP_NS2 domain.ru SOA +short
Если ответ есть, BIND работает локально.
17. Проверка DNS с внешнего компьютера
С рабочего компьютера или любого стороннего сервера:
dig @IP_NS1 domain.ru SOA +short
dig @IP_NS2 domain.ru SOA +short
Оба сервера должны вернуть SOA-запись.
Например:
ns1.domain.ru. root.domain.ru. 2026052004 7200 3600 1209600 180
Проверяем NS-записи:
dig @IP_NS1 domain.ru NS +short
dig @IP_NS2 domain.ru NS +short
Ожидаемый результат:
ns1.domain.ru.
ns2.domain.ru.
Проверяем публичное делегирование:
dig domain.ru NS +short
Ожидаемый результат:
ns1.domain.ru.
ns2.domain.ru.
Проверяем записи DNS-серверов:
dig ns1.domain.ru A +short
dig ns2.domain.ru A +short
dig host.domain.ru A +short
Ожидаемо:
IP_NS1
IP_NS2
IP_HOST
18. Возможная проблема: BIND отдаёт SERVFAIL на одном slave
Иногда зона в HestiaCP есть, команда v-list-dns-domains её показывает, named-checkconf -z говорит, что зона загружается, но при запросе:
dig @127.0.0.1 domain.ru SOA
получаем:
status: SERVFAIL
Проверяем статус зоны:
rndc zonestatus domain.ru
Если ответ:
zone not loaded
смотрим журнал BIND:
journalctl -u named -n 80 --no-pager
или:
journalctl -u bind9 -n 80 --no-pager
Если там есть ошибка вида:
loading from master file /home/dns-ns1/conf/dns/domain.ru.db failed: permission denied
zone domain.ru/IN: not loaded due to errors
то проблема может быть не в правах файла, а в AppArmor.
Проверяем AppArmor:
journalctl -k -g 'apparmor.*named|DENIED.*named' -n 50 --no-pager
Если видим:
apparmor="DENIED" operation="open" profile="named" name="/home/dns-ns1/conf/dns/domain.ru.db"
разрешаем BIND читать зоны HestiaCP.
Для ns1, если DNS-пользователь называется dns-ns1:
printf '\n# Hestia DNS zones\n/home/dns-ns1/conf/dns/** r,\n' >> /etc/apparmor.d/local/usr.sbin.named
apparmor_parser -r /etc/apparmor.d/usr.sbin.named
systemctl restart named
Для ns2, если DNS-пользователь называется dns-ns2:
printf '\n# Hestia DNS zones\n/home/dns-ns2/conf/dns/** r,\n' >> /etc/apparmor.d/local/usr.sbin.named
apparmor_parser -r /etc/apparmor.d/usr.sbin.named
systemctl restart named
После этого снова проверяем:
dig @127.0.0.1 domain.ru SOA +short
Если SOA-запись появилась, проблема устранена.
19. Возможная проблема: DNS работает локально, но не отвечает снаружи
Бывает ситуация, когда на самом DNS-сервере команда работает:
dig @IP_NS2 domain.ru SOA +short
а с внешнего компьютера:
dig @IP_NS2 domain.ru SOA +short
возвращает timeout.
Проверяем TCP:
dig +tcp @IP_NS2 domain.ru SOA +short
Если UDP timeout, а TCP connection reset или тоже не работает, нужно проверить firewall.
Смотрим правила Hestia:
/usr/local/hestia/bin/v-list-firewall
Должны быть открыты:
53 TCP
53 UDP
Но иногда на сервере дополнительно работает UFW, и правила Hestia не попадают в ufw-user-input.
Проверяем:
iptables -L INPUT -n -v --line-numbers
iptables -L ufw-user-input -n -v --line-numbers
Если в ufw-user-input нет правил для 53 порта, добавляем:
ufw allow 53
ufw reload
После этого снова проверяем с внешнего компьютера:
dig @IP_NS1 domain.ru SOA +short
dig @IP_NS2 domain.ru SOA +short
Если оба сервера отвечают — DNS работает правильно.
Также проверьте firewall в панели провайдера VPS. Некоторые провайдеры имеют отдельный сетевой firewall вне сервера. Там тоже должны быть открыты:
53 UDP
53 TCP
20. Настройка SSL для панелей HestiaCP
Когда DNS уже работает и записи указывают на правильные IP, можно выпустить Let’s Encrypt сертификаты для всех трёх панелей.
На master-сервере:
/usr/local/hestia/bin/v-add-letsencrypt-host
Панель будет доступна по:
https://host.domain.ru:8083
На ns1.domain.ru:
/usr/local/hestia/bin/v-add-letsencrypt-host
Панель:
https://ns1.domain.ru:8083
На ns2.domain.ru:
/usr/local/hestia/bin/v-add-letsencrypt-host
Панель:
https://ns2.domain.ru:8083
Перед выпуском сертификатов проверьте:
dig host.domain.ru A +short
dig ns1.domain.ru A +short
dig ns2.domain.ru A +short
Ожидаемо:
IP_HOST
IP_NS1
IP_NS2
Также порт 80/tcp должен быть доступен извне, иначе проверка Let’s Encrypt может не пройти.
21. Финальная проверка всей схемы
С внешнего компьютера:
dig domain.ru NS +short
Должно быть:
ns1.domain.ru.
ns2.domain.ru.
Проверяем glue/A-записи:
dig ns1.domain.ru A +short
dig ns2.domain.ru A +short
dig host.domain.ru A +short
Должно быть:
IP_NS1
IP_NS2
IP_HOST
Проверяем ответы обоих DNS-серверов:
dig @IP_NS1 domain.ru SOA +short
dig @IP_NS2 domain.ru SOA +short
Оба должны вернуть SOA-запись.
Проверяем NS с каждого сервера:
dig @IP_NS1 domain.ru NS +short
dig @IP_NS2 domain.ru NS +short
Ожидаемо:
ns1.domain.ru.
ns2.domain.ru.
Проверяем запись панели:
dig host.domain.ru A +short
Ожидаемо:
IP_HOST
22. Итог
В результате получается такая схема:
Пользователь создаёт домен в HestiaCP на host.domain.ru
↓
HestiaCP создаёт DNS-зону на master-сервере
↓
Master синхронизирует зону на ns1.domain.ru и ns2.domain.ru
↓
В интернете за домены отвечают ns1.domain.ru и ns2.domain.ru
Для клиентов указываются только NS:
ns1.domain.ru
ns2.domain.ru
А управление DNS-зонами остаётся в одной панели HestiaCP на master-сервере.
Главные моменты, которые нужно проверить после установки:
1. У регистратора созданы glue records для ns1 и ns2.
2. Домен domain.ru делегирован на ns1.domain.ru и ns2.domain.ru.
3. На master включён BIND и разрешён transfer на IP slave-серверов.
4. На slave-серверах включён BIND и разрешён notify от master.
5. В HestiaCP включён режим hestia-zone.
6. API на slave-серверах включён и разрешает IP master-сервера.
7. Порт 8083 доступен с master до slave-серверов.
8. Порты 53 TCP и 53 UDP открыты наружу на ns1 и ns2.
9. AppArmor не блокирует чтение DNS-зон из /home/dns-user/conf/dns/.
10. Оба DNS-сервера отвечают на dig-запросы извне.
Если все проверки проходят, HestiaCP DNS-кластер master/slave настроен корректно.