Как я навел порядок в домашней лаборатории: избавляемся от IP-адресов и портов
Мой хомлаб начинался с малого, и раздражение тоже было небольшим. Чтобы открыть Jellyfin, я вводил 192.168.0.x:8097. Для Home Assistant использовался другой IP и :8123. Для Karakeep, Ollama и остальных сервисов приходилось запоминать или добавлять в закладки еще больше комбинаций IP-адресов и портов. При этом закладки тоже работали ненадежно. Мой ZimaCube получал IP-адрес от роутера, как и любое другое устройство. Стоило заменить кабель или перезапустить подключение, как аппарат чаще всего возвращался с новым IP, ломая каждую закладку и конфигурацию, указывающую на него. Для Jellyfin это особенно ужасно, потому что ввод полной комбинации IP-адреса и номера порта с пульта телевизора напоминает средневековые пытки.

Поэтому в один из выходных я решил это исправить. Я начал с назначения выделенных IP-адресов для моих устройств Zima (ZimaBoard и ZimaCube), но в итоге провел полную чистку сети. Теперь моя система работает стабильно, а все регулярные службы функционируют в домене .internal. Теперь я просто ввожу jellyfin.internal в браузере. Никаких IP-адресов и номеров портов. Это спасло меня от кризиса среднего возраста.
Моя сеть до и после
Вот как выглядела моя сеть до наведения порядка. Роутер от интернет-провайдера передает сигнал на роутер TP-Link, который управляет сетью хомлаба, а узел OneMesh расширяет зону покрытия Wi-Fi. ZimaBoard потребляет меньше энергии и запускает такие службы, как Jellyfin, которые должны работать постоянно. ZimaCube — мощное устройство, и благодаря графическому процессору Nvidia Ada RTX я использую его для локального исследования искусственного интеллекта. Чтобы уменьшить счета за электричество, я включаю его только тогда, когда это необходимо.
А вот как это выглядит теперь. Оба устройства Zima имеют фиксированные IP-адреса, ZimaBoard отвечает за DNS и обратный прокси-сервер, а каждый сервис получил правильное имя. Идея в двух словах: AdGuard в качестве DNS и Nginx Proxy Manager. Вся система опирается на два компонента, работающих в связке. AdGuard Home, работающий как DNS-сервер сети, преобразует имя вроде jellyfin.internal в IP-адрес. Затем Nginx Proxy Manager проверяет запрошенное имя и перенаправляет запрос на нужный порт, поскольку AdGuard может работать только с IP-адресом, но не с номерами портов.
Как только это настроено, добавление нового сервиса превращается в процедуру из двух шагов: одно перенаправление DNS в AdGuard и один прокси-хост в Nginx Proxy Manager. Вот и все.
Шаг 1: Назначаем базовым устройствам фиксированные IP-адреса
Все в этой схеме зависит от IP-адресов, которые никогда не меняются. Если IP-адрес DNS-сервера изменится, вся система рухнет. Поэтому первой задачей стала настройка DHCP-резервирования на роутере. На моем маршрутизаторе TP-Link это находится по пути: Advanced -> Network -> DHCP Server -> Address Reservation. Система действительно показывает подключенное устройство и дает возможность зарезервировать IP прямо оттуда. Правда, так бывает не на всех роутерах.

В таком случае вы можете найти MAC-адрес в Linux с помощью команды: ip link show. Она покажет MAC-адрес текущего устройства. Вы можете использовать сетевую команду вроде arp, чтобы отсканировать MAC-адреса других устройств, подключенных к вашей сети. И все же лучше всего для этого подходит роутер, так как он в любом случае видит все подключенные к сети устройства. Я зарезервировал адрес 192.168.0.10, чтобы ни один телефон или ноутбук случайно не занял зарезервированный мной IP. Вот к какой схеме адресации я в итоге пришел: я также назначил фиксированные IP для Raspberry Pi и других одноплатных компьютеров в этой конфигурации. Они используются для запуска локальных ИИ-инструментов, таких как агенты Nanoclaw и Hermes. Кроме того, я настраиваю Frigate для камер. Своим опытом по поводу этих вещей я поделюсь в какой-нибудь из следующих статей.
Обратите внимание, что некоторым устройствам может потребоваться перезагрузка или обновление аренды DHCP, чтобы подхватить зарезервированный IP.
Шаг 2: Устанавливаем AdGuard Home на постоянно работающее устройство
AdGuard Home становится DNS-сервером для всей сети хомлаба, поэтому он должен работать постоянно. Моя ZimaBoard работает круглосуточно, в отличие от ZimaCube, так что выбор был простым. В магазине приложений ZimaOS App Store я установил вариант AdGuard Home (HOST), а не обычный. Сетевой режим хоста (Host networking) позволяет AdGuard напрямую привязываться к 53-му порту и видеть реальные IP-адреса клиентов. При использовании стандартной мостовой сети (bridge) Docker трафик проходит через NAT контейнера, и вы теряете оба этих преимущества.

Во время установки появляется всплывающее окно с подсказкой и скриптом конфигурации. Команда wget в моем случае завершилась ошибкой «Can’t be verbose and quiet at the same time», поэтому я запустил тот же скрипт с помощью curl: sudo bash -c «$(curl -fsSL https://raw.githubusercontent.com/bigbeartechworld/big-bear-scripts/master/generate-adguard-home-config/run.sh)». Не пропустите здесь sudo. Без него скрипт не сможет создать каталоги, но все равно выведет сообщение об успешном завершении.
Я принял путь конфигурации по умолчанию, перезапустил приложение из панели управления ZimaOS и вручную открыл http://192.168.0.4:3000. Клик по иконке приложения для приложений в хост-режиме не работает.
Что делать, если порты уже заняты
Мастер установки запрашивает административный порт и порт DNS, и оба они с чем-то конфликтуют. Порт 80 для веб-интерфейса был занят, поэтому я установил админ-панель AdGuard на порт 3786. Порт 53 оказался более неожиданным сюрпризом. Необычно, верно? Оказалось, у меня работал контейнер Pi-hole, о котором я совершенно забыл.

Настройка AdGuard и организация доменных имен в локальной сети
Оптимизация сетевых сервисов
Для поиска запущенных контейнеров и используемых ими портов я использовал команду sudo docker ps —format «{{.Names}}: {{.Ports}}». Поскольку Pi-hole и AdGuard выполняют идентичные задачи, держать оба сервиса одновременно не имело смысла, поэтому я удалил Pi-hole.
Настройка маршрутизатора для работы с AdGuard
После запуска AdGuard необходимо было направить на него весь сетевой трафик. В настройках моего маршрутизатора TP-Link я перешел в раздел Advanced -> Network -> Internet, раскрыл Advanced Settings и изменил параметры DNS на «Use the Following DNS Addresses». В качестве основного DNS-адреса был указан IP-адрес ZimaBoard с работающим AdGuard — 192.168.0.4, а в качестве резервного (secondary) — 1.1.1.1.
При такой конфигурации устройства продолжают использовать маршрутизатор в качестве DNS-сервера, а тот, в свою очередь, пересылает запросы на AdGuard. Именно поэтому в логах AdGuard большинство запросов отображаются как исходящие от маршрутизатора, а не от отдельных устройств. Для получения детальной статистики по каждому устройству можно указать IP-адрес AdGuard в настройках DHCP-сервера, чтобы маршрутизатор передавал эти данные устройствам напрямую. Использование 1.1.1.1 в качестве вторичного DNS необходимо для того, чтобы интернет продолжал работать, если ZimaBoard будет отключен. Однако у этого подхода есть минус: DNS-клиенты не всегда дожидаются ответа от основного сервера, прежде чем обратиться к вторичному. Если запрос попадет на Cloudflare, реклама не будет заблокирована, а внутренние доменные имена (.internal) не разрешатся, так как Cloudflare ничего о них не знает.

Устранение проблем с DNS на клиентах
В процессе тестирования я вручную прописал 192.168.0.4 в сетевых настройках GNOME на ноутбуке с Linux. Команда dig @192.168.0.4 google.com работала корректно, но трафик из браузера в логах AdGuard не отображался. Проверка через resolvectl status показала, что система продолжала использовать маршрутизатор, так как GNOME не применил изменения к активному соединению. Переподключение к Wi-Fi решило проблему. Более надежный способ настройки через nmcli выглядит так:
- nmcli connection modify «» ipv4.dns «192.168.0.4»
- nmcli connection modify «» ipv4.ignore-auto-dns yes
- nmcli connection down «» && nmcli connection up «»
Использование DNS-перезаписей и выбор доменной зоны
Для создания хостнеймов в AdGuard необходимо перейти в Filters -> DNS rewrites -> Add DNS rewrite, ввести домен (например, jellyfin.internal) и указать IP-адрес. Однако ZimaBoard обслуживает множество сервисов, а DNS-перезапись принимает только IP, но не номера портов. Если направить jellyfin.internal и homeassistant.internal на один IP 192.168.0.4, они не будут корректно разрешаться. Указывать zimaboard.internal:8097 технически можно, но это противоречит самой идее использования красивых имен. Для правильной маршрутизации по портам необходим обратный прокси-сервер в дополнение к DNS.
При выборе доменной зоны я отказался от .local, так как она зарезервирована для mDNS (Bonjour, Avahi), что может приводить к конфликтам. Варианты .lan и .home не зарезервированы официально и могут стать публичными доменами в будущем. Зона .home.arpa — официальная, но неудобная в наборе. В 2024 году ICANN официально зарезервировала .internal для частных сетей: это короткое, читаемое имя, которое никогда не пересечется с реальными сайтами.

Настройка Nginx Proxy Manager
Поскольку DNS переводит только имя в IP, для работы http://jellyfin.internal без указания порта :8097 нужен обратный прокси. Я выбрал Nginx Proxy Manager (NPM), так как он предлагает удобный веб-интерфейс, в отличие от Caddy, отсутствующего в ZimaOS в виде приложения «в один клик». NPM требует порты 80, 443 и 81 (для административной панели). Порт 80 был занят процессом zimaos-gateway, что я подтвердил командой sudo ss -tulpn | grep :80. Чтобы не использовать нестандартные порты, я изменил порт панели ZimaOS на 8888 в настройках системы. После перезагрузки ZimaBoard проверка порта прошла успешно, и я смог получить доступ к интерфейсу NPM по адресу http://192.168.0.4:81. Стандартные учетные данные (admin@example.com / changeme) при первом входе потребовали смены на более безопасные.
Настройка прокси-хостов и устранение неполадок
Для добавления новой службы перейдите в раздел Hosts -> Proxy Hosts -> Add Proxy Host и внесите необходимые данные. В поле Domain Names укажите имя хоста, например, jellyfin.internal. В качестве схемы выберите http. В строке Forward Hostname / IP введите IP-адрес устройства, на котором запущена служба, а в Forward Port — актуальный порт службы, например, 8097. Обязательно включите опцию Websockets Support: она необходима для работы Jellyfin и Home Assistant (поддержка обновлений в реальном времени), а поскольку она не вредит другим сервисам, её стоит держать включенной всегда. После сохранения настроек откройте http://jellyfin.internal в новой вкладке — сервис будет доступен без указания номера порта.
Решение возникающих проблем
Настройка сети не всегда проходит гладко. В ходе работы я столкнулся с рядом трудностей, две из которых зафиксировал.

Ошибка 400 в Home Assistant
Всё работало корректно, за исключением Home Assistant, который при обращении через homeassistant.internal возвращал ошибку 400: Bad Request, хотя прямой доступ по порту 8123 работал исправно. Home Assistant отклоняет проксируемые запросы в целях защиты от поддельных заголовков, если прокси явно не доверен. Для исправления нужно изменить файл configuration.yaml:
http:
use_x_forwarded_for: true
trusted_proxies:
— 172.17.0.3 # IP-адрес контейнера NPM в мостовой сети Docker
IP-адрес контейнера NPM можно узнать командой sudo docker inspect nginxproxymanager | grep IPAddress, после чего необходимо перезапустить Home Assistant командой sudo docker restart homeassistant. Важный нюанс: IP-адрес может измениться при пересоздании контейнера NPM. Чтобы этого избежать, можно добавить в доверенные всю подсеть моста (172.17.0.0/16).
Проблемы с Netflix на телевизоре
После смены DNS приложение Netflix на смарт-ТВ перестало подключаться. Лог запросов AdGuard показал блокировку двух доменов: logs.netflix.com и nrdp26.logs.netflix.com. Хотя это телеметрические эндпоинты, приложение использует их для проверки соединения. Вы можете разблокировать их через меню логов в AdGuard или добавить правила в Filters -> Custom filtering rules:

- @@||logs.netflix.com^
- @@||nrdp26.logs.netflix.com^
После перезапуска приложения на телевизоре всё должно заработать.
Управление конфликтами портов
В процессе проекта конфликты портов возникали трижды. Чтобы узнать, какой процесс использует порт, воспользуйтесь командой: sudo ss -tulpn | grep :. Имя процесса в выводе укажет на источник проблемы. Если указан docker-proxy, определите, какому контейнеру он принадлежит: sudo docker ps —format «{{.Names}}: {{.Ports}}». После этого решите: удалить конфликтующий контейнер или перенести службу на другой порт. Главное — не менять порт сервиса, который вы пытаетесь избавить от привязки к порту, например, NPM.
Важно помнить: вся эта схема работает только внутри домашней сети. Имена формата .internal существуют только в вашем AdGuard, поэтому они не будут разрешаться снаружи, если вы не используете VPN.

Добавление новых сервисов
В этом и заключается главное преимущество системы. При развертывании Karakeep мне потребовалось всего два шага:
- В AdGuard добавить DNS-перезапись: karakeep.internal, указывающую на ZimaBoard (192.168.0.4).
- В Nginx Proxy Manager добавить прокси-хост: karakeep.internal с перенаправлением на IP и порт службы (в моем случае — 14592).
Если служба имеет собственную проверку хоста, как Home Assistant, ей также может потребоваться предоставить доверие прокси.
Всё описанное выше функционирует стабильно уже несколько месяцев. В планах — внедрение HTTPS для имен .internal. Конечно, существуют и другие, возможно, более совершенные способы организации, но данная конфигурация полностью решает мою задачу: больше не нужно запоминать IP-адреса или номера портов в домашней лаборатории.






