О чем эта инструкция и когда она реально нужна
Если у вас случалось, что контейнеры внезапно перестают резолвить имена, обращения к сервисам по имени из docker compose работают через раз, а HTTP-запросы «висят» без явной причины — почти всегда проблема в одном из трех слоев: адресация (IPAM), DNS внутри Docker и MTU/PMTU на пути пакетов.
Ниже — практичный runbook: что именно проверять, какими командами, какие симптомы к чему относятся и как чинить точечно, не ломая остальные сети на хосте.
Фокус — на стандартном драйвере bridge (то, что вы чаще всего получаете в Compose), и на встроенном резолвере Docker 127.0.0.11. Отдельно разберем mtu, потому что «таймауты без ошибок» и «падает только загрузка больших файлов» часто оказываются именно PMTU blackhole.
Быстрая карта симптомов: DNS vs IPAM vs MTU
Когда похоже на DNS
- В контейнере
curl http://serviceвозвращаетCould not resolve host. - Внутренние имена Compose-сервисов перестают резолвиться (например,
db,redis). - Резолвинг «флапает»: то работает, то нет (часто при нагрузке).
Когда похоже на IPAM / пересечение подсетей
- Контейнеры не ходят в корпоративную сеть/VPN, хотя «вроде все настроено».
- После поднятия Compose пропал доступ к удаленной подсети даже с хоста.
- Появляются странные маршруты, NAT «не туда», коллизии из-за одинаковых подсетей.
Когда похоже на MTU / PMTU blackhole
- Мелкие запросы проходят, а большие ответы/загрузки зависают или рвутся.
- Часть HTTPS-страниц не догружается, зависает
git clone,apt, загрузка артефактов. - В логах приложений таймауты, а в сетевых логах «почти пусто».
Базовая модель сети Docker: что важно помнить
В типовом docker compose (драйвер bridge) Docker создает Linux-мост (например, br-xxxxxxxx), добавляет к нему veth-интерфейсы контейнеров и настраивает правила NAT/форвардинга в firewall.
Сервисные имена в Compose резолвятся через встроенный DNS Docker. Внутри контейнера вы обычно увидите в /etc/resolv.conf адрес 127.0.0.11 — это локальный stub-резолвер, который знает имена контейнеров в сети и форвардит внешние запросы на upstream DNS, взятые с хоста (или заданные явно).
Адреса контейнерам назначает IPAM (IP Address Management) — подсети, диапазоны, gateway. Если подсеть Docker пересеклась с «реальной» сетью (VPN, офис, облако), вы получите неочевидные эффекты: пакеты начнут уходить «в мост» вместо реального маршрута.
Шаг 1. Собираем факты: сети, подсети, DNS и MTU прямо сейчас
Проверяем сети и их параметры
docker network ls
docker network inspect bridge
Для compose-сети найдите ее имя (часто project_default) и посмотрите IPAM.Config, Options, Internal, Attachable.
docker network inspect project_default
Проверяем, что видит контейнер
docker exec -it your_container cat /etc/resolv.conf
Если там есть nameserver 127.0.0.11 — используется Docker DNS. Сразу обратите внимание на options ndots: большое значение вместе с длинным search иногда дает задержки резолвинга.
Проверяем MTU
Нужно знать MTU на трех уровнях: uplink-интерфейс хоста, мост Docker и интерфейс внутри контейнера.
ip link show
Ищите uplink (например, eth0/ens3), мост docker0 или br-..., а также veth-пары. Если uplink MTU 1450 (часто в overlay/VXLAN), а мост/контейнеры на 1500 — это кандидат №1 на «необъяснимые таймауты».

IPAM в Docker: как не словить пересечение подсетей
Почему пересечение подсетей ломает все тихо
Docker по умолчанию выбирает подсети автоматически из приватных диапазонов. Если у вас на хосте поднят VPN или уже есть маршруты в 10.0.0.0/8 или 172.16.0.0/12, Docker может случайно взять «ту же» подсеть под свой bridge. Тогда пакет до реального ресурса (например, 10.10.0.5) начнет уходить в docker-мост и пропадать.
Типичный признак IPAM-конфликта: с контейнера не открывается ресурс в корпоративной сети, а с хоста поведение тоже ухудшается после запуска Docker сетей.
Как быстро найти пересечения
Сначала смотрим таблицу маршрутизации хоста.
ip route
Затем — подсети всех docker-сетей. Вывод может быть большим, но пересечения обычно заметны по полям Subnet и Gateway.
docker network inspect $(docker network ls -q)
Как задать подсеть явно в Docker Compose
Самый надежный путь — явно описать сеть в compose.yaml и выбрать подсеть, которая точно ни с чем не пересекается в вашей инфраструктуре.
services:
app:
image: your/app
networks:
backend:
db:
image: postgres:16
networks:
backend:
networks:
backend:
driver: bridge
ipam:
config:
- subnet: 172.30.50.0/24
gateway: 172.30.50.1
Плюсы: предсказуемо и удобно для диагностики. Минус: нужно вести минимальный адресный план (хотя бы избегать пересечений с VPN/облаком/офисом).
Статические IP: используйте только когда есть веская причина
Статические IP в Compose чаще всего не нужны: правильнее ходить по DNS-именам сервисов. Но если нужно для легаси-интеграций, делается так:
services:
legacy:
image: your/legacy
networks:
backend:
ipv4_address: 172.30.50.10
networks:
backend:
driver: bridge
ipam:
config:
- subnet: 172.30.50.0/24
Если контейнеров много, статические адреса быстро превращаются в ручной учет и источник конфликтов.
DNS в Docker: что такое 127.0.0.11 и почему резолвинг иногда ломается
Как работает Docker DNS
127.0.0.11 внутри контейнера — это DNS-слой Docker, который:
- резолвит имена контейнеров в пределах сети;
- поддерживает network aliases;
- форвардит остальное во внешние резолверы (обычно те, что настроены на хосте).
Частая проблема — upstream DNS на хосте: systemd-resolved со stub 127.0.0.53, корпоративный DNS через VPN и split-horizon, нестабильные резолверы, особенности firewall для UDP/53.
Проверяем резолвинг внутри контейнера правильно
Начните с проверки, что имя сервиса Compose резолвится (это проверка service discovery, не «интернета»):
docker exec -it your_container getent hosts db
Если не резолвится, сначала убедитесь, что контейнеры действительно в одной сети и вы используете имя сервиса (а не надеетесь на container_name).
Дальше проверяйте внешние домены:
docker exec -it your_container getent ahosts example.com
Если внутренние имена работают, а внешние — нет, значит Docker DNS не может форвардить во внешние резолверы или те не отвечают.
Частые причины “container cannot resolve”
- Контейнеры не в одной сети. В Compose это бывает при нескольких сетях и забытом
networks:у части сервисов. - Проблемный upstream DNS на хосте. Docker берет список DNS с хоста; если там stub/нестабильная связка, ошибки проявятся в контейнерах.
- Firewall/Policy блокирует UDP 53 или ICMP. Иногда проходит только TCP/53 или наоборот.
- Неудачные
search/ndots. Длинные search-домены вместе сndotsмогут давать задержки и лишние запросы.
Практичные фиксы DNS в Compose
1) Явно задать DNS для сервиса, чтобы отвязаться от настроек хоста:
services:
app:
image: your/app
dns:
- 1.1.1.1
- 8.8.8.8
2) Явно задать опции резолвера, если подозреваете ndots:
services:
app:
image: your/app
dns_opt:
- ndots:1
- timeout:2
- attempts:2
3) Если вам нужен стабильный split-DNS (разные зоны через разные резолверы), чаще всего надежнее решать это на уровне хоста (нормальный резолвер и маршрутизация), а в Docker отдавать контейнерам один предсказуемый DNS.
Если у вас еще нет нормального контроля за отказоустойчивостью контейнеров, пригодится отдельный материал про проверки и перезапуски: healthcheck и restart policy в Docker Compose.
MTU в Docker: почему “все висит”, хотя пинги идут
Откуда берется проблема MTU
Если провайдер/туннель/overlay снижает MTU (1450, 1400, 1380), а контейнеры считают, что MTU 1500, то большие пакеты требуют фрагментации. При корректной PMTUD сеть должна прислать ICMP “Fragmentation needed”, и отправитель уменьшит размер пакета. Но если ICMP режут, получается PMTU blackhole: большой пакет не проходит, обратной подсказки нет, соединение «подвисает».
Как проверить MTU-патологию быстрыми тестами
На хосте проверьте «нефрагментируемый» ping до внешнего адреса (IPv4):
ping -M do -s 1472 1.1.1.1
Если не проходит — уменьшайте -s, пока не начнет проходить. Максимальное значение -s плюс 28 байт заголовков (ICMP+IP) примерно соответствует MTU пути. Аналогично тестируйте из контейнера, если в образе есть ping.
Также сравните MTU uplink и docker-моста:
ip link show docker0
ip link show | grep -E '^[0-9]+: br-'
Если мост 1500, а uplink 1450 — это явный красный флаг.
Как задать MTU для Docker и Compose
Вариант А (обычно правильнее): системно для Docker daemon, если проблема общая для всех сетей. Проверьте текущий файл и задайте mtu:
cat /etc/docker/daemon.json
{
"mtu": 1450
}
После изменения перезапустите Docker:
systemctl restart docker
Вариант Б: только для конкретной сети через driver options (удобно для отдельных проектов):
networks:
backend:
driver: bridge
driver_opts:
com.docker.network.driver.mtu: "1450"
Важно: MTU не всегда применится к уже созданной сети. Часто нужно пересоздать сеть: остановить проект и поднять заново.
MSS clamping как временный workaround
Если вы не можете быстро повлиять на прохождение ICMP для PMTUD, временным workaround может быть clamp MSS на выходе (на уровне firewall хоста). Это уменьшает TCP сегменты так, чтобы они точно укладывались в MTU. Но правильнее сначала починить первопричину: выставить корректный mtu в Docker и не блокировать нужные ICMP-сообщения на пути.

Bridge network: типовые ловушки и правила хорошего тона
Не смешивайте default bridge и проектные сети без причины
Сеть bridge по умолчанию удобна для тестов, но в проде лучше иметь явную сеть проекта: проще инспектировать, проще задавать IPAM/MTU/DNS, проще держать порядок.
Проверяйте, что сервисы действительно в одной сети
Самая банальная причина “не резолвится” — сервисы в разных сетях. Быстро проверяется так:
docker inspect -f '{{json .NetworkSettings.Networks}}' your_container
Убедитесь, что у двух контейнеров есть общий ключ сети.
DNS-имя сервиса и имя контейнера — не одно и то же
В Compose обычно используется DNS-имя сервиса. Если вы задаете container_name и ожидаете, что оно станет стабильным DNS-именем во всех сценариях, можно получить сюрпризы. В большинстве случаев держитесь сервисных имен и алиасов сети.
Runbook: пошаговая диагностика за 10 минут
Определить масштаб: проблема в одном контейнере, в одной сети или во всем Docker на хосте.
Проверить service discovery:
docker exec -it your_container getent hosts db.Проверить внешние домены:
docker exec -it your_container getent ahosts example.com. Если не работает — смотреть upstream DNS на хосте и/или задатьdnsв Compose.Посмотреть
/etc/resolv.confконтейнера: есть ли127.0.0.11, какиеsearchиndots.Проверить подсети Docker:
docker network inspectи сравнить сip route. Искать пересечения.Сверить MTU uplink vs bridge:
ip link show. При сомнениях — тестping -M do -s ....Применить точечный фикс: IPAM в Compose, DNS в Compose, MTU в daemon или в сети.
Частые вопросы и острые углы
Почему в контейнере DNS один, а на хосте другой?
Потому что внутри контейнера работает stub Docker (127.0.0.11), а на хосте может быть другой stub (например, 127.0.0.53). Docker берет upstream-резолверы с хоста, но обслуживает контейнеры своим компонентом. Поэтому диагностика идет по цепочке: контейнер → 127.0.0.11 → upstream.
Можно ли полностью отключить 127.0.0.11?
Обычно цель не «отключить», а сделать upstream DNS предсказуемым. Если вы задаете dns в Compose, Docker при этом сохраняет сервис-дискавери для имен внутри сети. Полное отключение ломает резолвинг сервисных имен.
Почему MTU проблема проявляется только на некоторых сайтах/запросах?
Потому что маленькие пакеты проходят всегда. Ломается там, где больше payload, другая TLS-фрагментация, другой путь (например, CDN) или просто иной размер TCP сегментов.
Итог
Для стабильной сети в Docker/Compose держите в голове три опоры: адресный план (IPAM без пересечений), предсказуемый DNS (понимание роли 127.0.0.11) и корректный MTU (с учетом туннелей/overlay). Почти любой кейс из серии container cannot resolve или «таймауты без причин» раскладывается по этим направлениям, если идти по runbook и проверять факты.
Если вы разворачиваете Docker-проекты на сервере, где важны предсказуемые сети и доступ к VPN/корпоративным подсетям, удобнее делать это на VDS: там проще контролировать маршруты, MTU и firewall-правила, чем на «черном ящике».


