HomeLab.net.ua

Твоє залізо. Твої правила. Твоя мова.

Pi-hole у Docker: як підняти мережевий DNS-блокувальник без зламаного резолвінгу

Опубліковано Оновлено

pi-hole docker

Pi-hole Docker – це спосіб винести DNS-блокування реклами на рівень усієї мережі, не ставлячи сервіс напряму в систему хоста. У homelab такий варіант дає ізоляцію, простіше оновлення і чистіший rollback. Нижче – робоча схема розгортання, вибір мережевого режиму, перевірка резолвінгу і місця, де Pi-hole у Docker зазвичай ламається.

Практичний результат тут простий: клієнти в LAN починають ходити на Pi-hole як на основний DNS, запити фільтруються на рівні доменів, а сам контейнер не конфліктує з локальним resolver на хості. Найбільше проблем не з фільтрацією, а з мережею Docker, портом 53 і неправильним DHCP або DNS на маршрутизаторі.

Pi-hole добре працює як DNS sinkhole для домашньої інфраструктури, тестових VLAN і невеликих self-hosted мереж. Але якщо в мережі вже є складний локальний DNS, split-horizon з кількома зонами або жорстка прив’язка до існуючого resolver, контейнеризація потребує акуратнішої схеми, інакше починаються дивні таймаути та петлі резолвінгу.

Який мережевий режим вибрати для Pi-hole у Docker

Для Pi-hole у Docker вибір мережевого режиму важливіший за сам контейнер. Якщо потрібен просто DNS-сервер для LAN, найменше сюрпризів дає host або окрема IP-адреса через macvlan. Режим bridge теж працює, але частіше створює проблеми з портами, видимістю клієнтів і додатковими правилами NAT.

host зручний там, де хост не використовує власний DNS на порту 53. Контейнер слухає мережу хоста без пробросу портів, а клієнти бачать сервіс напряму. Мінус очевидний: якщо на машині вже працює systemd-resolved, dnsmasq або інший локальний resolver, конфлікт майже гарантований.

bridge має сенс, коли потрібна стандартна модель Docker і контроль портів. Але DNS через NAT – не найелегантніша історія. Частина запитів працює штатно, частина діагностики стає незручною, а в складніших схемах з conditional forwarding це лише додає шуму.

macvlan дає контейнеру окрему адресу в LAN. Для Pi-hole це майже ідеальний компроміс: немає конфлікту з DNS хоста, сервіс виглядає як окремий вузол, клієнтський трафік видно чистіше. Нюанс у тому, що хост зазвичай не може напряму ходити до контейнера через той самий macvlan без додаткової мережевої схеми. У homelab це часто спливає вже після розгортання, коли веб-інтерфейс із самого сервера раптом недоступний.

Як розгорнути Pi-hole у Docker через docker compose

Найпростіша схема для старту – окремий каталог, постійні томи для конфігурації і проброс DNS та веб-інтерфейсу. Якщо на хості вже зайнятий порт 53, спочатку прибирають локальний resolver з цього порту, а вже потім піднімають контейнер.

mkdir -p ~/pihole
cd ~/pihole

cat > compose.yml <<'EOF'
services:
  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    restart: unless-stopped
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8080:80/tcp"
    environment:
      TZ: "Europe/Kyiv"
      FTLCONF_webserver_api_password: "changeme"
    volumes:
      - ./etc-pihole:/etc/pihole
      - ./etc-dnsmasq.d:/etc/dnsmasq.d
    cap_add:
      - NET_ADMIN
EOF

docker compose up -d

Такий варіант підходить для стартового стенда або для хоста без локального DNS на 53 порту. Веб-інтерфейс у цьому прикладі винесений на 8080, щоб не зачепити вже зайнятий 80 порт. Якщо хост чистий, порт можна залишити стандартним.

Після запуску перевіряють, чи контейнер справді слухає DNS:

docker ps
ss -lntup | grep :53
curl -I http://127.0.0.1:8080/admin/

Далі на маршрутизаторі або DHCP-сервері як DNS для клієнтів виставляють IP вузла з Pi-hole. Сам контейнер ще не означає, що мережа почне ним користуватись. Без зміни DNS для клієнтів блокування реклами залишиться локальним експериментом.

Чому Pi-hole у Docker конфліктує з портом 53

Конфлікт із портом 53 – найтиповіша проблема при запуску Pi-hole у Docker. На Linux-хості цей порт часто вже зайнятий локальним resolver. Docker тут ні до чого: контейнер просто не може прив’язатися до сокета, який уже використовується іншою службою.

Найчастіше винен systemd-resolved. На Debian, Ubuntu та похідних він слухає локальний DNS stub resolver. Перевірка проста:

ss -lntup | grep :53
systemctl status systemd-resolved

Якщо порт зайнятий, є кілька шляхів. Перший – перевести хост на резолвінг без локального stub і звільнити 53 порт. Другий – не використовувати host/ports на цьому хості, а дати Pi-hole окрему адресу через macvlan. Третій – винести сервіс на іншу машину. Для постійного homelab найчистіший варіант часто саме окрема IP-адреса, а не боротьба з локальною DNS-службою хоста.

Тут є неочевидний момент. Навіть якщо контейнер стартував, хост може все одно продовжити резолвити через старий локальний resolver, а клієнти – через Pi-hole. У результаті частина перевірок покаже, що блокування працює, а частина – що ні. Це не помилка Pi-hole, а дві різні DNS-траєкторії в одній системі.

Що змінити на маршрутизаторі, щоб блокування працювало для всієї мережі

Щоб Pi-hole у Docker реально блокував рекламу на рівні мережі, клієнти мають отримати саме його як DNS-сервер. Найправильніше це робити через DHCP на маршрутизаторі або окремому DHCP-сервері. Ручна зміна DNS на кожному пристрої працює, але для LAN це тимчасовий обхід, а не нормальна схема.

У типовому сценарії достатньо вказати IP Pi-hole як primary DNS у DHCP-опціях. Якщо є secondary DNS, частина клієнтів піде повз фільтрацію. Це стара проблема, але її регулярно недооцінюють: другий DNS не є резервом у звичному сенсі, деякі клієнти використовують його паралельно. Якщо потрібне повне доменне блокування, сторонній secondary DNS краще не видавати.

Для локальних імен у homelab часто залишають upstream DNS на маршрутизатор, Unbound або локальний recursive resolver. Якщо в мережі є внутрішні домени, краще відразу продумати, хто авторитетний за локальні записи. Pi-hole як фронт до локального DNS працює нормально, але як єдине місце для всього резолвінгу він швидко починає обростати винятками.

Як перевірити, що Pi-hole у Docker працює коректно

Перевірка має бути двошаровою: окремо доступність DNS-сервісу, окремо факт фільтрації. Якщо перевіряти лише веб-інтерфейс, можна пропустити зламаний UDP 53. Якщо дивитись лише на блокування доменів, легко не помітити, що частина клієнтів взагалі обходить Pi-hole у Docker.

Спочатку перевіряють резолвінг напряму через IP сервера:

dig @192.168.1.10 example.org
nslookup example.org 192.168.1.10

Потім дивляться, чи клієнт реально використовує потрібний DNS. На Linux це видно через resolvectl або /etc/resolv.conf, на маршрутизаторі – через lease, на самому Pi-hole – через query log у веб-інтерфейсі. Якщо клієнт не з’являється в логах, проблема не в списках блокування, а в шляху DNS-трафіку.

Окремо варто перевірити кілька типів доменів: звичайний домен, відомий рекламний домен і локальне ім’я з homelab. Саме останній тест часто ламає гарну картинку. Блокування працює, а локальні записи – ні, бо upstream DNS не знає про внутрішню зону.

Де Pi-hole у Docker найчастіше ламається в homelab

У homelab Pi-hole у Docker найчастіше ламається не через контейнер, а через суміжні сервіси. Типові збої – подвійний DNS, некоректний DHCP, неправильний upstream resolver і спроба поєднати bridge-мережу з очікуванням, що все виглядатиме як окремий фізичний вузол.

  • Клієнти отримують два DNS-сервери, і частина запитів минає фільтрацію.
  • Хост використовує один resolver, а мережа – інший, тому тести дають суперечливий результат.
  • Локальні домени не резолвляться, бо Pi-hole не передає їх на потрібний upstream.
  • Контейнер піднятий у bridge, але від нього очікують поведінку окремого LAN-хоста.
  • Після оновлення Docker або хоста міняються мережеві правила, і UDP 53 починає поводитись нестабільно.

Є ще одна межа застосовності. Якщо в мережі кілька VLAN, окремі політики DNS, split DNS для VPN-клієнтів і власний authoritative DNS, Pi-hole у Docker перестає бути центральним рішенням і стає лише фільтруючим шаром. На цьому етапі краще розділити ролі: окремо резолвер, окремо рекурсія, окремо блокування. Інакше будь-яка дрібна зміна в LAN зачіпає одразу весь DNS-ланцюг.

Коли замість Pi-hole у Docker краще вибрати інший підхід

Pi-hole у Docker має сенс там, де потрібне швидке мережеве блокування реклами, базовий контроль DNS і просте обслуговування. Якщо ж основна задача – повний локальний DNS для складної інфраструктури, контейнер із Pi-hole краще не ставити в центр усієї схеми. Він добре фільтрує, але не замінює продуману DNS-архітектуру.

Для маленького homelab це майже оптимальний варіант. Для мережі з кількома підмережами, VPN і купою внутрішніх сервісів часто краще ставити Pi-hole перед окремим recursive resolver або інтегрувати його тільки як фільтр. Це грубіше на старті, зате далі менше дивних ефектів із локальними зонами й політиками маршрутизації DNS.

Офіційний сайт проєкту: Pi-hole. Контейнерний образ і приклади розгортання: pihole/pihole на Docker Hub.