Выберите продукт

Docker published ports в Debian/Ubuntu: как исправить too many colons in address и bind address already in use

Разбираем типичные ошибки при публикации портов Docker в Debian и Ubuntu: неверный синтаксис параметра -p, too many colons in address, конфликт занятых портов, IPv4/IPv6 и быструю диагностику через ss, lsof, docker ps и inspect.
Docker published ports в Debian/Ubuntu: как исправить too many colons in address и bind address already in use

Публикация портов в Docker кажется простой ровно до первого сбоя. Обычно всё начинается с команды вроде docker run -p 8080:80 nginx:stable, а заканчивается сообщениями too many colons in address или bind address already in use. На Debian и Ubuntu эти ошибки встречаются регулярно: где-то ломается синтаксис -p, где-то контейнер пытаются привязать к уже занятому порту, а где-то мешает путаница между IPv4 и IPv6.

Проблема в том, что снаружи последствия похожи: контейнер не стартует как ожидалось, сервис недоступен, а ощущение такое, будто Docker «просто не открыл порт». Но причины у этих ошибок разные, и лечатся они тоже по-разному. Если действовать наугад, легко потерять время на перезапуск демона, пересоздание контейнеров и правку compose-файлов без реального результата.

Ниже разберём, как Docker читает published ports, почему он ругается на лишние двоеточия, откуда берётся конфликт уже занятого адреса и чем это всё быстро проверять на Debian и Ubuntu.

Как Docker понимает published ports

Базовая форма публикации порта выглядит как HOST_PORT:CONTAINER_PORT. Например, запись 8080:80 означает, что порт 80 внутри контейнера будет доступен на порту 8080 хоста.

Более полная форма — HOST_IP:HOST_PORT:CONTAINER_PORT. Она нужна, если вы хотите ограничить публикацию конкретным адресом, например только loopback-интерфейсом:

docker run -d --name web -p 127.0.0.1:8080:80 nginx:stable

Такой вариант удобен, если сервис должен быть доступен только локально, например за reverse proxy. Но именно здесь часто начинается путаница с IPv6: адрес IPv6 сам содержит двоеточия, а Docker тоже использует двоеточие как разделитель полей.

Если в параметре -p фигурирует IPv6, адрес почти всегда нужно заключать в квадратные скобки. Иначе Docker не поймёт, где IP, а где порт.

Почему появляется too many colons in address

Сообщение too many colons in address означает, что Docker не смог корректно распарсить значение в -p или в секции ports compose-файла. На практике самая частая причина — IPv6-адрес без квадратных скобок.

Типичная ошибка с IPv6

Неправильный пример:

docker run -d --name web -p 2001:db8::10:8080:80 nginx:stable

Для человека структура ещё угадывается, а для парсера Docker уже нет: двоеточий слишком много, и он не понимает, где заканчивается адрес.

Правильный вариант:

docker run -d --name web -p [2001:db8::10]:8080:80 nginx:stable

Скобки однозначно отделяют IPv6-адрес от номера порта. Тот же принцип действует и в compose.

Когда ошибка не в IPv6, а в смешении форматов

Иногда проблема появляется из-за попытки скрестить несколько форм записи сразу. Например, пользователь добавляет лишнее поле через двоеточие или случайно оставляет пустую часть между разделителями.

docker run -p 127.0.0.1:80:80:tcp nginx:stable
docker run -p :::8080:80 nginx:stable
docker run -p 0.0.0.0::80 nginx:stable

Во всех этих случаях Docker либо сообщит о лишних двоеточиях, либо вернёт ошибку парсинга published port. Если нужен протокол, он указывается как суффикс, например 8080:80/tcp.

Docker Compose и YAML

В compose ошибка может выглядеть менее очевидно, потому что запись часто хранится в строке. Надёжный вариант для IPv4 и IPv6 такой:

services:
  web:
    image: nginx:stable
    ports:
      - "127.0.0.1:8080:80"
      - "[2001:db8::10]:8443:443"

Для IPv6 в compose лучше всегда оставлять значение в кавычках и со скобками. Это избавляет от двусмысленности на уровне YAML и самого Docker.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Что означает bind address already in use

Ошибка bind address already in use означает уже не проблему синтаксиса, а конфликт на хосте: Docker пытается занять конкретный адрес и порт, но они уже используются другим процессом или контейнером.

На Debian и Ubuntu это чаще всего происходит в таких сценариях:

  • порт 80 или 443 уже занят Nginx, Apache или другим reverse proxy;
  • тот же host port уже опубликован другим контейнером;
  • после рестарта или сбоя осталась старая привязка;
  • есть конфликт между IPv4 и IPv6, когда процесс слушает слишком широко;
  • systemd-служба стартовала раньше Docker и заняла нужный порт.

Важно помнить: Docker не делает порт доступным «сам по себе». Он создаёт привязку на стороне хоста. Если привязка уже существует, контейнер не сможет стартовать с тем же published port.

Проверка занятых портов на сервере через ss и lsof

Как быстро найти, кто занял порт

Первый инструмент на Debian и Ubuntu — ss:

ss -ltnp
ss -ltnp '( sport = :80 )'
ss -ltnp '( sport = :443 )'
ss -ltnp '( sport = :8080 )'

Если нужен вывод с PID и именем процесса, используйте lsof:

lsof -iTCP:80 -sTCP:LISTEN -n -P
lsof -iTCP:443 -sTCP:LISTEN -n -P
lsof -iTCP:8080 -sTCP:LISTEN -n -P

Если подозрение на другой контейнер, проверьте Docker:

docker ps --format 'table {{.Names}} {{.Ports}}'
docker port CONTAINER_NAME

Если у вас уже были сложности с сетевой фильтрацией хоста, дополнительно полезно посмотреть, как Docker взаимодействует с firewall в материале про Docker, iptables и nftables.

Разница между конфликтом на IPv4 и IPv6

Один из неприятных случаев — когда кажется, что порт свободен, но Docker всё равно пишет bind address already in use. Часто это связано с тем, как процесс слушает сокет IPv6. На Linux приложение может слушать :: и при определённых настройках принимать также IPv4-подключения.

Поэтому проверять надо оба семейства адресов. Команда ss -ltnp обычно сразу показывает, на чём висит слушатель: 0.0.0.0:80, 127.0.0.1:8080 или [::]:80. Если сервис слушает [::]:80, это не значит, что IPv4 гарантированно свободен.

Практический вывод простой: публикация на конкретный адрес, например 127.0.0.1:8080:80, обычно предсказуемее, чем публикация на все интерфейсы через 8080:80.

Примеры безопасной публикации

Если сервис нужен только локально:

docker run -d --name app -p 127.0.0.1:8080:80 nginx:stable

Если нужен только IPv6 loopback:

docker run -d --name app -p [::1]:8080:80 nginx:stable

Если нужно слушать все интерфейсы:

docker run -d --name app -p 8080:80 nginx:stable

Последний вариант самый удобный для быстрых тестов, но именно он чаще всего упирается в конфликты.

Пошаговая диагностика на Debian и Ubuntu

Когда контейнер не стартует из-за публикации порта, лучше идти по короткому чек-листу, а не менять всё подряд.

  1. Проверьте синтаксис -p или секции ports. Если есть IPv6, убедитесь, что адрес в квадратных скобках.
  2. Проверьте, свободен ли порт через ss и lsof.
  3. Посмотрите, не занят ли тот же порт другим контейнером через docker ps.
  4. Убедитесь, что старый контейнер не завис в цикле рестартов.
  5. Проверьте, какие адреса реально слушает системный сервис: только IPv4, только IPv6 или оба варианта.
  6. Если проблема в compose-проекте, посмотрите docker inspect и параметры сети.

Набор команд для полной картины:

docker ps -a
docker inspect CONTAINER_NAME
docker network ls
docker network inspect bridge
journalctl -u docker --no-pager -n 100

Особенно полезен journalctl: демон Docker часто пишет туда точную причину сбоя, включая адрес и порт bind-привязки.

Если контейнеры запускаются на отдельном сервере, удобнее и безопаснее делать такую диагностику на VDS, где проще контролировать сетевой стек, firewall и список сервисов на хосте.

Частые сценарии и как их исправить

Nginx или Apache уже занял 80/443

Очень частый сценарий: на хосте уже работает веб-сервер, а вы запускаете контейнер с -p 80:80 или -p 443:443. Docker получает конфликт и завершает запуск.

Обычно помогает один из трёх вариантов:

  • публиковать контейнер на другой порт, например 127.0.0.1:8080:80;
  • освободить 80 и 443, если трафик должен принимать именно контейнер;
  • оставить фронтовой веб-сервер на хосте и проксировать запросы в контейнер по loopback.

Другой контейнер уже опубликовал тот же порт

В нескольких compose-проектах это происходит постоянно: один стек уже слушает 8080 или 3000, а второй пытается занять тот же порт.

docker ps --format 'table {{.Names}} {{.Ports}}'

Исправление простое: меняете host port или убираете лишнюю публикацию у контейнера, которому внешний доступ не нужен.

Ошибка из-за IPv6 в compose

Если в compose-файле есть строка без скобок вроде 2001:db8::1:8080:80, получите too many colons in address ещё до нормального старта контейнера.

services:
  app:
    image: nginx:stable
    ports:
      - "[2001:db8::1]:8080:80"

Порт как будто свободен, но контейнер не стартует

Тут часто виноваты dual stack, systemd socket units или остаточные процессы после перезапуска служб.

systemctl list-units --type=socket --all
systemctl status nginx apache2 docker
ss -ltnp | grep ':8080 '
ss -ltnp | grep ':80 '
ss -ltnp | grep ':443 '

Если случай редкий и плавающий, смотрите также логи Docker и журнал системы. В спорных ситуациях помогает поочерёдно выключить кандидатов и повторить запуск контейнера.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Как правильно работать с IPv6 в Docker

Здесь часто смешивают две разные темы: публикацию порта контейнера на IPv6-адресе хоста и полноценную работу IPv6 внутри Docker-сетей. Для ошибки too many colons in address обычно важна именно первая часть — корректная запись bind-адреса.

Если контейнер должен быть доступен по IPv6, проверьте четыре вещи:

  • IPv6 реально настроен на интерфейсе хоста;
  • firewall пропускает нужный порт;
  • Docker получил адрес в правильном формате со скобками;
  • само приложение внутри контейнера слушает нужный контейнерный порт.

Проверочные команды:

ip -6 addr show
ss -ltnp
docker inspect --format '{{json .NetworkSettings.Ports}}' CONTAINER_NAME

Если порт опубликован, но извне доступа нет, проблема уже не в синтаксисе публикации, а в маршрутизации, firewall или конфигурации приложения.

Для более жёстких требований к изоляции контейнеров может пригодиться разбор изоляции контейнеров через gVisor и Firecracker, особенно если на одном хосте работает несколько независимых сервисов.

Пример публикации порта Docker с IPv6-адресом в квадратных скобках

Что лучше: publish на все интерфейсы или только localhost

С точки зрения безопасности и предсказуемости чаще выигрывает публикация на 127.0.0.1 или ::1 с фронтом через reverse proxy. Так меньше конфликтов, проще контролировать доступ и ниже риск случайно выставить служебный интерфейс наружу.

Публикация на все интерфейсы удобна для быстрых тестов, но в production именно она чаще приводит к вопросам вроде «кто уже занял порт» и «почему сервис неожиданно доступен извне».

Если контейнеру не нужен прямой доступ из интернета, публикуйте его на loopback и выводите наружу только через один контролируемый прокси-слой.

Короткий runbook: что делать за 2 минуты

  1. Увидели too many colons in address — проверьте формат -p и квадратные скобки у IPv6.
  2. Увидели bind address already in use — сразу смотрите ss -ltnp и docker ps.
  3. Если порт занят веб-сервером хоста, переводите контейнер на локальный порт вроде 127.0.0.1:8080:80.
  4. Если конфликтует другой контейнер, меняйте host port или убирайте лишнюю публикацию.
  5. Если проблема в IPv6, проверьте адрес на интерфейсе и правила firewall.
  6. Если всё ещё непонятно, открывайте journalctl -u docker и docker inspect.

Итог

Ошибки too many colons in address и bind address already in use относятся к одной теме — публикации портов Docker, но означают разное. Первая почти всегда указывает на неправильный синтаксис, особенно при работе с IPv6. Вторая говорит о реальном конфликте bind-привязки на хосте.

На Debian и Ubuntu лучший подход простой: сначала проверить формат записи port mapping, затем занятость порта, потом поведение IPv4 и IPv6. Такой порядок закрывает большинство кейсов без лишней магии.

Если держать в голове две идеи — IPv6 в publish пишется в квадратных скобках и один host port нельзя занять дважды — подобные ошибки обычно решаются за несколько минут.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: rp_filter, reverse path filtering и policy routing без сюрпризов OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: rp_filter, reverse path filtering и policy routing без сюрпризов

Разбираем, как в Debian/Ubuntu работает reverse path filtering, почему в dmesg появляются martian source и как правильно сочетать ...
Debian/Ubuntu: как исправить overlayfs invalid argument и operation not permitted в rootless Docker OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить overlayfs invalid argument и operation not permitted в rootless Docker

Если rootless Docker на Debian или Ubuntu падает с ошибками overlayfs invalid argument или operation not permitted, причина обычно ...
Debian/Ubuntu: как исправить Apache AH00558 Could not reliably determine the server's fully qualified domain name OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Apache AH00558 Could not reliably determine the server's fully qualified domain name

Предупреждение Apache AH00558 на Debian и Ubuntu обычно не ломает сайт, но показывает, что серверу не хватает базовой настройки. Р ...