Если вы активно используете Docker на Windows 11 (особенно в связке с WSL2 или Hyper-V), вы наверняка хотя бы раз ловили странную ошибку при попытке пробросить порт:
Error response from daemon: ports are not available: exposing port TCP 0.0.0.0:18080 -> 127.0.0.1:0:
listen tcp 0.0.0.0:18080: bind: An attempt was made to access a socket in a way forbidden by its access permissions.
Первое, что делает любой разработчик в такой ситуации — открывает терминал и проверяет, кто занял порт:
netstat -aon | findstr PORT
А в ответ — тишина. Никакой процесс не слушает этот порт. И тем не менее Docker упорно отказывается его занимать. Дело в том, что проблема здесь вообще не в занятости порта — она на уровне операционной системы.
В чём корень проблемы
Сообщение «An attempt was made to access a socket in a way forbidden by its access permissions» означает, что порт заблокирован самой Windows. Виновник — служба WinNAT (Windows NAT-драйвер), которую используют Hyper-V и WSL2 для организации сетевого взаимодействия с виртуальными машинами.
Чтобы виртуализация работала корректно, Windows резервирует под свои нужды так называемые excluded port ranges — диапазоны исключений среди динамических портов. По умолчанию эти диапазоны могут начинаться с произвольной позиции: с 1024, с 10000 или вообще откуда-то из середины. Если ваш рабочий порт (8080, 3000, 5432, 18080 — что угодно) случайно попадает в один из таких интервалов, Windows просто запретит любому стороннему софту, включая Docker, к нему прикасаться.
Самое неприятное — после каждой перезагрузки Windows может переразмечать эти диапазоны заново, и проблема «гуляет» от сессии к сессии.
Шаг 1. Диагностика
Чтобы убедиться, что причина именно в резервировании, откройте PowerShell от имени Администратора и выполните:
netsh int ipv4 show excludedportrange protocol=tcp
На экране появится таблица с диапазонами портов, которые Windows считает «своими»:
Протокол tcp, диапазоны исключений портов
Стартовый порт Конечный порт
-------------- -----------
17005 17104
18000 18100 <- вот тут застрял ваш например 18080
50000 50059
Если нужный порт попадает в любой из этих интервалов — система его не отдаст. Переходим к решению.
Вариант 1. Быстрый костыль на один раз
Если контейнер нужно поднять прямо сейчас, а разбираться с настройками некогда, можно принудительно перезапустить службу NAT. WinNAT при перезапуске сбросит текущие резервации и выделит новые — скорее всего, в другом месте, и нужный порт освободится.
Откройте PowerShell от имени Администратора:
# Останавливаем WinNAT (уже запущенные контейнеры временно потеряют сеть)
net stop winnat
# Запускаем контейнер, пока порт свободен
docker compose up -d
# Возвращаем службу NAT на место
net start winnat
Минус: после следующей перезагрузки Windows снова может забрать ваш порт. Так что это именно «побыстрее починить», а не решение.
Вариант 2. Решение навсегда
Чтобы забыть о проблеме, нужно явно сказать Windows, в каком диапазоне ей разрешено выделять динамические порты под свои нужды. Сдвинем этот диапазон в самый конец — туда, где локальные сервисы разработчиков обычно не работают.
Запускаем PowerShell от имени Администратора и выполняем три команды:
# 1. Останавливаем службу NAT, которая держит порты
net stop winnat
# 2. Сдвигаем диапазон динамических портов в конец TCP-пространства
netsh int ipv4 set dynamicport tcp start=49152 num=16384
# 3. Возвращаем WinNAT в работу
net start winnat
Что произошло
Мы установили стартовую точку динамических портов на 49152 и выделили под системные нужды 16384 порта — ровно до верхней границы TCP-диапазона (65535). Всё, что ниже 49152, теперь гарантированно свободно от посягательств Windows: популярные 80, 443, 3000, 3306, 5432, 8080, 8443, 18080 — все остаются за вами и вашими приложениями.
Настройка переживает перезагрузки и обновления Windows, потому что записывается в реестр.
Как убедиться, что всё сработало
После выполнения команд снова запросите список диапазонов исключений:
netsh int ipv4 show excludedportrange protocol=tcp
Все интервалы теперь должны стартовать с порта 49152 или выше. Можно спокойно пробрасывать любые «разработческие» порты в Docker.
Краткая шпаргалка
- Виновник: WinNAT (Hyper-V / WSL2) резервирует динамические порты.
- Диагностика:
netsh int ipv4 show excludedportrange protocol=tcp. - Быстрый фикс:
net stop winnat→docker compose up→net start winnat. - Навсегда:
netsh int ipv4 set dynamicport tcp start=49152 num=16384(между stop/start WinNAT).
Одна минута в PowerShell — и Docker больше никогда не споткнётся о зарезервированный Windows порт.
