BIND9 в Docker Compose: готовый файл
Если вы администрируете больше пары доменов или хотите держать собственный резолвер для внутренней сети, рано или поздно упираетесь в BIND9 — он старый, местами неудобный, но именно он де-факто стандарт индустрии, и почти вся документация RFC по DNS написана с оглядкой на его поведение. Ставить его «в систему» через apt — плохая идея на современном сервере: версия в репозитории отстаёт, конфиги расползаются по /etc, а откатить неудачный zone-файл без даунтайма неудобно. Ниже — рабочий docker-compose.yml, который поднимает BIND9 как авторитетный DNS-сервер за пару минут и позволяет держать все зоны и конфиги в одной папке под git.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Почему BIND9, а не dnsmasq или PowerDNS
BIND9 — это не «ещё один DNS-сервер», а эталонная реализация, на которую ориентируются другие проекты. У него есть три вещи, которых часто не хватает более лёгким альтернативам: полноценная поддержка DNSSEC из коробки, гибкие ACL на уровне view (можно отдавать разные ответы внутренним и внешним клиентам с одного сервера) и зрелая система логирования запросов через rndc и категории логов.
Если вам нужен просто быстрый рекурсивный резолвер для дома или небольшой сети — присмотритесь к AdGuard Home в Docker Compose, он проще в администрировании. Если вы уже работаете с PowerDNS и вас устраивает его REST API и SQL-бэкенд — менять шило на мыло смысла нет. BIND9 выбирают, когда нужна максимальная совместимость с корпоративными практиками, DNSSEC-подпись зон вручную или сложные ACL с несколькими views на одном IP.
Структура проекта
Прежде чем писать docker-compose.yml, разложим файлы так, чтобы конфиг и зоны версионировались отдельно от рантайма:
bind9/
├── docker-compose.yml
├── config/
│ ├── named.conf
│ ├── named.conf.options
│ ├── named.conf.local
│ └── zones/
│ └── db.example.com
├── cache/
└── logs/
Папки cache/ и logs/ монтируются как volume — так журналы и кэш переживут пересоздание контейнера, а config/ держим под git, чтобы откатить неудачный zone-файл было делом одной команды.
mkdir -p bind9/{config/zones,cache,logs}
cd bind9
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверГотовый docker-compose.yml
Используем образ internetsystemsconsortium/bind9 — это официальная сборка от ISC, разработчика самого BIND. На момент написания статьи (конец августа 2026) актуальна ветка BIND 9.18 LTS — уточняйте тег на Docker Hub перед деплоем, версии там обновляются.
services:
bind9:
image: internetsystemsconsortium/bind9:9.18
container_name: bind9
restart: unless-stopped
ports:
- "53:53/tcp"
- "53:53/udp"
# rndc-порт наружу не публикуем — управляем изнутри сети docker
volumes:
- ./config:/etc/bind
- ./cache:/var/cache/bind
- ./logs:/var/log/bind
environment:
- TZ=Europe/Moscow
cap_add:
- NET_BIND_SERVICE
networks:
- dns_net
networks:
dns_net:
driver: bridge
Обратите внимание на cap_add: NET_BIND_SERVICE — без него контейнер не сможет забиндиться на 53-й порт без privileged: true, а полный privileged-режим для DNS-сервера, торчащего наружу, — плохая идея с точки зрения безопасности.
Базовый конфиг named.conf.options
Файл config/named.conf.options отвечает за общее поведение сервера: рекурсию, ACL, логирование запросов. Начните с максимально закрытой конфигурации и открывайте только то, что реально нужно:
acl "trusted" {
10.0.0.0/8;
172.16.0.0/12;
localhost;
};
options {
directory "/var/cache/bind";
pid-file "/var/run/named/named.pid";
recursion yes;
allow-recursion { trusted; };
allow-query { trusted; any; };
allow-transfer { none; };
listen-on { any; };
listen-on-v6 { none; };
dnssec-validation auto;
querylog yes;
};
logging {
channel query_log {
file "/var/log/bind/query.log" versions 5 size 20m;
severity info;
print-time yes;
};
category queries { query_log; };
};
Ключевые моменты, о которых часто забывают:
allow-recursionдолжен быть ограничен доверенными сетями — открытый рекурсивный резолвер на публичном IP превращается в участника DNS-амплификации для DDoS-атак третьих лиц, и это не гипотетическая угроза, а то, за что провайдер может забанить сервер.allow-transfer { none; }в options блокирует зонный трансфер по умолчанию, а разрешаем его точечно уже в описании конкретной зоны для конкретного slave-сервера.querylog yesполезен на старте для отладки, но на проде с большим трафиком генерирует много I/O — на VPS с медленным диском лучше снизить severity или использоватьsizeс ротацией пожёстче.
Конфигурация зоны
Файл config/named.conf.local подключает зоны:
zone "example.com" {
type master;
file "/etc/bind/zones/db.example.com";
allow-transfer { 203.0.113.10; }; // slave-сервер
also-notify { 203.0.113.10; };
};
А сам zone-файл config/zones/db.example.com — классический формат BIND, с serial, который обязательно инкрементить при каждом изменении (иначе slave-серверы не подхватят обновление):
$TTL 3600
@ IN SOA ns1.example.com. admin.example.com. (
2026083101 ; Serial (год-месяц-день-номер)
7200 ; Refresh
3600 ; Retry
1209600 ; Expire
3600 ) ; Minimum TTL
IN NS ns1.example.com.
IN NS ns2.example.com.
IN MX 10 mail.example.com.
@ IN A 203.0.113.5
ns1 IN A 203.0.113.5
ns2 IN A 203.0.113.6
www IN A 203.0.113.5
mail IN A 203.0.113.7
Формат serial ГГГГММДД+номер — не требование протокола, а удобная конвенция: сразу видно, когда зона правилась в последний раз, и не нужно держать в голове счётчик.
Запуск, проверка синтаксиса и первые тесты
Перед стартом контейнера стоит проверить конфиг статически — BIND9 предоставляет для этого утилиты named-checkconf и named-checkzone, они есть внутри образа:
docker compose run --rm bind9 named-checkconf /etc/bind/named.conf
docker compose run --rm bind9 named-checkzone example.com /etc/bind/zones/db.example.com
Если обе команды не выдали ошибок, поднимаем сервис:
docker compose up -d
docker compose logs -f bind9
Проверяем резолвинг снаружи контейнера — dig покажет и ответ, и время, за которое сервер его отдал:
dig @127.0.0.1 example.com A +short
dig @127.0.0.1 www.example.com A
Если сервер стоит на VPS с внешним IP и вы хотите проверить доступность именно с публичного адреса — то же самое с dig @ваш_публичный_ip. На этом этапе легко словить проблему, о которой ниже.
Firewall, порты и типичные грабли
DNS работает на 53-м порту сразу по TCP и UDP — TCP нужен для зонных трансферов и ответов, не влезающих в один UDP-пакет (например, при DNSSEC). Если вы уже настраивали ufw на сервере, не забудьте открыть оба протокола:
ufw allow 53/tcp
ufw allow 53/udp
Частые проблемы при первом запуске:
| Симптом | Причина | Решение | |
|---|---|---|---|
dig отдаёт connection refused | Порт не проброшен в firewall или занят другим процессом (systemd-resolved) | `ss -tulpn \ | grep :53`, освободить порт или отключить локальный резолвер |
Zone не грузится, в логах not loaded due to errors | Опечатка в zone-файле, чаще всего пропущенная точка в конце FQDN | named-checkzone укажет строку с ошибкой | |
| Внешние клиенты не резолвят домен | allow-query слишком узкий, либо провайдер блокирует исходящий 53/udp | Проверить ACL, для авторитетного сервера allow-query { any; }; — это нормально, ограничивать нужно allow-recursion | |
| Контейнер падает сразу после старта | systemd-resolved на хосте уже держит 53-й порт | Отключить systemd-resolved заглушку stub-listener или биндить BIND на другой интерфейс |
Отдельно стоит сказать про защиту от brute-force и сканирования: DNS-сервер редко атакуют перебором паролей (аутентификации там как таковой нет), но он часто становится целью для флуда запросами. Если вы уже используете fail2ban для других сервисов на этом сервере, для BIND есть готовые джейлы, которые банят IP по паттернам в query-логе при аномальной частоте запросов — это неплохая дополнительная линия обороны поверх ACL.
Резервное копирование конфигурации и зон
Так как конфиг и зоны лежат в обычных файлах на хосте (volume ./config), бэкап сводится к архивации папки и, в идеале, к git-репозиторию с историей изменений zone-файлов — это даёт бесплатный аудит «кто и когда поменял A-запись»:
cd bind9
git init
git add config/
git commit -m "initial BIND9 config"
Для регулярного бэкапа достаточно cron-задачи с архивацией и отправкой на удалённое хранилище:
0 3 * * * tar czf /backup/bind9-$(date +\%F).tar.gz -C /opt/bind9 config && find /backup -name 'bind9-*.tar.gz' -mtime +14 -delete
Перед откатом всегда прогоняйте named-checkconf/named-checkzone на восстановленных файлах — иначе есть риск поднять сервис с конфигом, который выглядит правильно, но не проходит валидацию из-за изменившейся версии BIND.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Нужен ли BIND9 в Docker или лучше ставить на голый сервер?
В контейнере проще версионировать конфиг, откатывать обновления образа и переносить сервис между серверами — сам сервис от этого не медленнее, оверхед Docker для DNS-трафика пренебрежимо мал.
Можно ли использовать этот же compose-файл как рекурсивный резолвер для локальной сети?
Да, для этого достаточно расширить allow-recursion на подсеть ваших клиентов и убрать секцию zone с master-зонами, если вам не нужно быть ещё и авторитетным сервером для собственных доменов.
Как включить DNSSEC-подпись зоны?
Нужно сгенерировать ключи через dnssec-keygen, подписать зону dnssec-signzone и подключить в конфиге уже подписанный .signed-файл — это отдельная тема, объём которой не укладывается в один compose-файл, ориентируйтесь на официальную документацию ISC для конкретной версии.
Чем BIND9 отличается от dns leak, который проверяют для VPN?
Это разные вещи: DNS leak — про то, куда уходят запросы клиента при активном VPN, а BIND9 здесь может выступать как раз тем доверенным резолвером, на который вы принудительно направляете трафик, чтобы утечки не было.
Сколько ресурсов нужно серверу под BIND9?
Для нескольких десятков доменов без большой рекурсивной нагрузки хватает 1 vCPU и 1 ГБ RAM с запасом — узкое место обычно не CPU, а сетевые лимиты и защита от флуда при паблик-резолвере.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →