Pi-hole в Docker Compose: готовый файл
Реклама и трекеры лезут из каждого приложения и сайта, а блокировщики в браузере не спасают телевизор, умную колонку или телефон вне дома. Pi-hole решает это на уровне DNS — режет запросы к рекламным и трекинговым доменам до того, как они долетят до устройства. Ниже — рабочий docker-compose.yml, который поднимает Pi-hole на VPS и закрывает главную дыру такой схемы: DNS-сервер, торчащий в интернет, за час превращается в мишень для DDoS-амплификации, если не ограничить к нему доступ.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Зачем Pi-hole на VPS, а не только дома
Классический сценарий — Pi-hole на Raspberry Pi в квартире, обслуживает роутер и все устройства локальной сети. Проблема в том, что за пределами дома (в кафе, в поездке, на работе) телефон снова видит рекламу: он использует DNS оператора или публичного Wi-Fi, а не ваш Pi-hole.
Если поднять Pi-hole на VPS и подключаться к нему через VPN, блокировка рекламы работает везде, где есть интернет — не только дома. Заодно решается вторая проблема: домашний интернет часто сидит за CGNAT или динамическим IP, а сервер с постоянным адресом и нормальным аплинком снимает эти ограничения.
Плата за удобство — сервер с открытым 53-м портом в чистом виде нельзя оставлять доступным всему интернету. Публичный рекурсивный DNS без ограничений — классический вектор для DNS amplification атак: злоумышленник подделывает IP жертвы в запросе, ваш сервер отвечает жертве потоком трафика в разы больше запроса. Поэтому в конфигурации ниже DNS-порт биндится не на все интерфейсы, а только на адрес VPN-туннеля.
| Сценарий | Плюсы | Минусы |
|---|---|---|
| Pi-hole дома на Raspberry Pi | Не платите за сервер, всё в локальной сети | Не работает вне дома, зависит от домашнего интернета |
| Pi-hole на VPS без ограничений | Просто поднять, доступен отовсюду | Открытый резолвер — риск абьюза и амплификации |
| Pi-hole на VPS + WireGuard | Работает везде, DNS закрыт для посторонних | Нужно настроить VPN-клиент на каждом устройстве |
Третий вариант — тот, который разворачиваем в этой статье.
Что понадобится перед стартом
Минимальный набор:
- VPS с 1 vCPU и 1 ГБ RAM — Pi-hole легковесный, больше не нужно даже с несколькими сотнями клиентов;
- установленный Docker и плагин Docker Compose (если ещё не ставили — есть отдельный разбор установки Docker на Ubuntu 24.04);
- поднятый WireGuard на том же сервере или рядом — без него DNS придётся либо закрывать фаерволом по IP (неудобно с мобильными устройствами и динамическими адресами), либо оставлять открытым (не вариант). Пошаговая установка есть в статье про WireGuard на Ubuntu 24.04;
- если клиентов будет много и хочется управлять ими через веб-интерфейс, а не руками редактировать конфиги — посмотрите на wg-easy как панель для WireGuard, она удобно сочетается с Pi-hole именно в такой схеме.
Проверьте, что порт 53 на хосте не занят системным systemd-resolved — на свежем Ubuntu он часто слушает 127.0.0.53:
sudo ss -tulpn | grep :53
Если видите systemd-resolved, это не помешает — Pi-hole в контейнере будет слушать другой адрес (адрес VPN-интерфейса), конфликта портов не возникнет. Конфликт возможен только если планируете забиндить Pi-hole на 0.0.0.0:53 — тогда придётся либо отключить systemd-resolved, либо оставить резолв через resolved в покое и не трогать 53-й порт на хосте вообще.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверГотовый docker-compose.yml
Создайте рабочую директорию и файл:
mkdir -p ~/pihole && cd ~/pihole
mkdir -p etc-pihole etc-dnsmasq.d
nano docker-compose.yml
Содержимое docker-compose.yml:
services:
pihole:
container_name: pihole
image: pihole/pihole:latest
hostname: pihole
restart: unless-stopped
environment:
TZ: "Europe/Moscow"
WEBPASSWORD_FILE: /run/secrets/pihole_password
PIHOLE_DNS_: "1.1.1.1;9.9.9.9"
DNSSEC: "true"
DNSMASQ_LISTENING: "all"
WEBTHEME: "default-dark"
volumes:
- ./etc-pihole:/etc/pihole
- ./etc-dnsmasq.d:/etc/dnsmasq.d
ports:
# адрес интерфейса WireGuard на сервере — замените на свой
- "10.13.13.1:53:53/tcp"
- "10.13.13.1:53:53/udp"
- "10.13.13.1:8080:80/tcp"
cap_add:
- NET_ADMIN
- SYS_TIME
- SYS_NICE
secrets:
- pihole_password
secrets:
pihole_password:
file: ./pihole_password.txt
Пароль для веб-интерфейса выносим в отдельный файл, а не в открытую переменную окружения — так он не светится в docker inspect и в выводе docker compose config:
openssl rand -base64 18 > pihole_password.txt
chmod 600 pihole_password.txt
Если у вас Pi-hole версии 6 (проект переработал систему конфигурации в 2025 году) — переменная для пароля веб-интерфейса могла смениться на FTLCONF_webserver_api_password. Перед первым запуском стоит свериться со страницей образа на Docker Hub (hub.docker.com/r/pihole/pihole) — там актуальный список переменных для установленного тега.
Про то, зачем volume-ы вынесены наружу контейнера, а не оставлены анонимными — отдельно разобрано в статье про типы Docker volumes: etc-pihole хранит базу gravity (списки блокировки) и настройки, etc-dnsmasq.d — кастомные DNS-конфиги. Без bind-mount обновление образа pihole/pihole:latest каждый раз обнуляло бы настройки.
Как не превратить Pi-hole в открытый DNS-резолвер
Это ключевой момент всей схемы. Три уровня защиты, имеет смысл включить все три, а не один:
1. Биндинг порта только на VPN-интерфейс. В docker-compose.yml выше порт 53 привязан к 10.13.13.1 — это IP-адрес сервера внутри туннеля WireGuard, а не публичный IP VPS. Снаружи, через публичный адрес сервера, DNS-запросы просто не долетят до Pi-hole — порт там не слушается. Проверить после запуска:
sudo ss -tulpn | grep 53
В выводе должен быть только адрес 10.13.13.1, не 0.0.0.0 и не публичный IP.
2. Фаервол на всякий случай. Даже с правильным биндингом не помешает явно закрыть 53-й порт на публичном интерфейсе средствами ufw:
sudo ufw deny in on eth0 to any port 53
sudo ufw allow in on wg0 to any port 53
Замените eth0 и wg0 на реальные имена интерфейсов из ip a.
3. Отключить DHCP и conditional forwarding, если не нужны. По умолчанию Pi-hole их не включает, но при ручной миграции настроек со старой установки бывает, что DHCP-сервер активируется и начинает конфликтовать с роутером. В веб-интерфейсе: Settings → DHCP — убедитесь, что переключатель выключен, если вы не используете Pi-hole как DHCP-сервер сети.
Если WireGuard ещё не настроен — сначала сделайте это, а потом возвращайтесь к Pi-hole: без туннеля вся эта схема попросту не имеет смысла с точки зрения безопасности.
Первый запуск и настройка веб-интерфейса
Поднимаем стек:
docker compose up -d
docker compose logs -f pihole
Дождитесь строки вида [i] FTL is ready, после чего интерфейс доступен по http://10.13.13.1:8080/admin — то есть только тем, кто подключён через WireGuard.
Первым делом внутри интерфейса:
Settings → DNS— включите один-два DNS-провайдера верхнего уровня (в примере выше уже заданы черезPIHOLE_DNS_, но можно поменять). Стоит выбрать провайдеров с поддержкой DNS-over-HTTPS, если хотите скрыть DNS-трафик самого сервера от вашего хостинг-провайдера или транзитных сетей.Settings → System— проверьте временную зону, она влияет на графики и логи запросов.Group Management → Adlists— здесь подключаются списки блокировки, о них ниже.- Смените пароль сразу после первого входа, если использовали временный из файла
pihole_password.txt.
Проверка с клиентского устройства, подключённого к WireGuard — задать DNS-сервер 10.13.13.1 в настройках сети и открыть в браузере pi.hole/admin или проверить резолв напрямую:
dig @10.13.13.1 doubleclick.net
Если Pi-hole блокирует домен, dig вернёт 0.0.0.0 или адрес из блок-страницы вместо реального IP.
Списки блокировки, upstream DNS и тонкая настройка
Из коробки Pi-hole идёт с базовым списком блокировки, но по-настоящему полезным он становится после добавления сторонних списков. В Group Management → Adlists добавляются URL, после чего запускается Tools → Update Gravity.
Ориентировочно (без гарантии точных цифр — списки регулярно обновляются, а объём зависит от версии):
| Список | Фокус |
|---|---|
| StevenBlack/hosts (unified) | Базовый набор reklamы + трекеров, хорошая стартовая точка |
| OISD (Big или NSFW-версии) | Широкое покрытие, регулярные обновления |
| AdAway hosts | Мобильная реклама, приложения |
| Список для конкретной платформы (Smart TV, Roku) | Точечная блокировка телеметрии устройств |
Не добавляйте сразу десяток списков — начните с двух-трёх, посмотрите на Query Log неделю, добавляйте дальше по необходимости. Слишком агрессивная блокировка иногда ломает легитимные сервисы (баннер для входа через капчу, виджеты оплаты), и разбираться, какой из десяти списков виноват, — не самое приятное занятие.
Автоматическое обновление gravity раз в неделю добавляется через cron на хосте:
0 4 * * 0 docker exec pihole pihole updateGravity >> /var/log/pihole-gravity.log 2>&1
Для upstream DNS вместо голых IP можно настроить DNS-over-HTTPS через cloudflared в соседнем контейнере того же compose-стека — тогда сам Pi-hole резолвит через зашифрованный канал, а не открытым текстом на 853/443 порт наружу. Это отдельная настройка, если тема интересна — обычно её добавляют вторым сервисом в тот же docker-compose.yml и переключают PIHOLE_DNS_ на 127.0.0.1#5053, где слушает cloudflared.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Pi-hole на VPS будет тормозить резолв из-за расстояния до сервера?
Заметная задержка добавляется, но обычно некритична для обычного браузинга — TTL закешированных ответов сглаживает повторные запросы. Если сервер и основная локация пользователя далеко друг от друга (например, сервер в США, а пользуетесь из России), лучше выбрать локацию ближе к месту, откуда чаще всего идёт трафик.
Что будет, если Pi-hole упадёт — интернет пропадёт совсем?
Да, если DNS-клиенты настроены только на Pi-hole и нет резервного адреса. Разумно указать вторичный DNS в настройках клиента (например, публичный резолвер) как fallback, либо настроить restart: unless-stopped в compose (уже есть в конфиге выше) и мониторинг контейнера.
Нужен ли статический публичный IP у VPS?
Для схемы через WireGuard — нужен статический (или хотя бы редко меняющийся) адрес именно для самого WireGuard-эндпоинта, к которому подключаются клиенты. Внутренний адрес Pi-hole (10.13.13.1 в примере) от публичного IP не зависит.
Можно ли обойтись без WireGuard и просто ограничить доступ по IP через ufw?
Технически можно, но это ломается на мобильных устройствах с динамическим внешним IP и не защищает трафик между клиентом и сервером от подмены DNS на маршруте. VPN закрывает оба вопроса разом.
Как перенести существующие настройки Pi-hole со старой установки?
В Settings → Teleporter есть экспорт/импорт полного бэкапа конфигурации — списки, локальные DNS-записи, настройки — одним архивом.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →