MAATRIX / Блог / Pi-hole в Docker Compose: готовый файл

Pi-hole в Docker Compose: готовый файл

MAATRIX

Реклама и трекеры лезут из каждого приложения и сайта, а блокировщики в браузере не спасают телевизор, умную колонку или телефон вне дома. 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.

Первым делом внутри интерфейса:

  1. Settings → DNS — включите один-два DNS-провайдера верхнего уровня (в примере выше уже заданы через PIHOLE_DNS_, но можно поменять). Стоит выбрать провайдеров с поддержкой DNS-over-HTTPS, если хотите скрыть DNS-трафик самого сервера от вашего хостинг-провайдера или транзитных сетей.
  2. Settings → System — проверьте временную зону, она влияет на графики и логи запросов.
  3. Group Management → Adlists — здесь подключаются списки блокировки, о них ниже.
  4. Смените пароль сразу после первого входа, если использовали временный из файла 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 ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →