Ошибка nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) в Debian и Ubuntu означает простую вещь: nginx пытается открыть сокет на порту, который уже занят другим процессом или другим экземпляром самого nginx. Тот же сценарий встречается и для 0.0.0.0:443, а также для IPv6-адресов вроде [::]:80.
На практике это одна из самых частых причин, почему после изменения конфигурации, установки Certbot, запуска контейнера или включения второго веб-сервера nginx перестаёт стартовать. Сообщение выглядит тревожно из-за уровня emerg, но обычно проблема диагностируется за несколько минут.
Главное правило — не останавливать сервисы наугад. Сначала нужно понять, кто именно слушает порт. Иначе можно «починить» nginx, но случайно уронить соседний проект или внутренний прокси.
Ниже разберём типовые сценарии для конфликтов на 80 и 443: Apache, остаточные процессы nginx, Docker, systemd socket activation и ошибки в конфигурации.
Базовый принцип здесь такой: один и тот же IP:порт обычно не могут одновременно занимать два независимых процесса. Значит, если nginx не может открыть 80 или 443, этот сокет уже кем-то занят либо конфликт создан внутри его собственной конфигурации.
Как выглядит проблема и что она означает
Обычно ошибка появляется при запуске или перезапуске сервиса:
sudo systemctl restart nginx
В ответ можно увидеть примерно следующее:
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
nginx: configuration file /etc/nginx/nginx.conf test failed
Код 98 — это системная ошибка EADDRINUSE, то есть адрес уже используется. Под адресом здесь понимается связка IP и порта. Если nginx настроен слушать все IPv4-интерфейсы через 0.0.0.0:80, а порт уже занят, сервис не стартует.
Есть важная тонкость: иногда кажется, что конфликт именно внешний, хотя фактически проблема вызвана дублем внутри конфигурации nginx. Например, два server-блока пытаются слушать один и тот же IP:порт с несовместимыми параметрами. Поэтому диагностика всегда состоит из двух шагов: проверить конфиг и проверить реальные процессы, которые держат сокет.
Первый шаг: проверить конфигурацию nginx
Перед поиском «чужого» процесса убедитесь, что сам nginx не ругается на синтаксис или дубли директив. Это самый быстрый и безопасный старт:
sudo nginx -t
Если тест конфигурации показывает только ошибку bind, идём дальше. Если есть дополнительные сообщения, сначала исправьте их. Часто проблема возникает из-за повторного подключения одного и того же сайта или неудачного копирования блока с директивой listen.
Полезно посмотреть и полный итоговый конфиг после всех include:
sudo nginx -T
Ищите все упоминания listen 80;, listen 443 ssl; и привязки к конкретным адресам. На больших конфигурациях удобнее сохранить вывод в файл и искать по нему отдельно.
sudo nginx -T > /tmp/nginx-full-config.txt
grep -R "listen 80" /etc/nginx
grep -R "listen 443" /etc/nginx
Если конфигурация выглядит корректно, переходим к главному вопросу: кто уже занял нужный порт.
Кто занял порт 80 или 443: быстрые команды диагностики
В Debian и Ubuntu удобнее всего использовать ss. Он быстро показывает TCP-сокеты в состоянии LISTEN и процессы, которым они принадлежат.
sudo ss -ltnp
Чтобы сразу отфильтровать нужные порты, выполните:
sudo ss -ltnp '( sport = :80 or sport = :443 )'
Типичный вывод может быть таким:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("apache2",pid=812,fd=4),("apache2",pid=813,fd=4))
LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("docker-proxy",pid=1442,fd=7))
Такой вывод сразу показывает источник конфликта: 80 занят Apache, а 443 опубликован контейнером через docker-proxy.
Второй полезный инструмент — lsof. Он особенно удобен, когда нужно быстро увидеть PID, пользователя и тип сокета.
sudo lsof -nP -iTCP:80 -sTCP:LISTEN
sudo lsof -nP -iTCP:443 -sTCP:LISTEN
Если lsof ещё не установлен, добавьте пакет:
sudo apt update
sudo apt install lsof
Практическое правило простое: сначала
nginx -t, потомss, потомlsof. Только после этого имеет смысл останавливать сервисы или править публикацию портов.

Частый сценарий №1: конфликт Apache и nginx
Самый частый случай — на сервере уже запущен Apache. Это бывает после установки LAMP-стека, панели управления, Certbot-плагина для Apache или просто из-за старой конфигурации, о которой давно забыли.
Сначала проверьте состояние:
sudo systemctl status apache2
sudo ss -ltnp '( sport = :80 or sport = :443 )'
Если Apache действительно слушает нужные порты, придётся определить, какой веб-сервер должен быть фронтендом. На одном IP и одном порту одновременно два обычных веб-сервера работать не будут.
- оставить только nginx;
- оставить только Apache;
- развести их по разным портам и использовать reverse proxy;
- развести их по разным IP-адресам.
Если основным фронтендом должен быть nginx, остановите Apache и отключите автозапуск:
sudo systemctl stop apache2
sudo systemctl disable apache2
sudo systemctl restart nginx
Если после этого nginx запускается, причина найдена. Но перед остановкой убедитесь, что Apache действительно не нужен другому сайту или внутреннему сервису.
Если вы как раз выбираете, какой сервер использовать в роли фронтенда, может пригодиться сравнение подходов в материале nginx и Apache для современных проектов.
Иногда Apache формально установлен, но не должен стартовать сам. Тогда полезно посмотреть, кто его включил:
sudo systemctl is-enabled apache2
sudo journalctl -u apache2 -b --no-pager
Частый сценарий №2: порт занят другим экземпляром nginx
Такое тоже бывает регулярно. Например, nginx уже работает под управлением systemd, а администратор запускает его вручную. Или после неудачного перезапуска остаются процессы, которые ещё держат сокет.
Проверьте состояние сервиса и список процессов:
sudo systemctl status nginx
ps -ef | grep nginx
Если nginx уже активен, вместо start часто нужен reload или restart:
sudo systemctl reload nginx
Если сервис завис в некорректном состоянии, действуйте аккуратно:
sudo systemctl stop nginx
sudo pkill -f 'nginx: master process'
sudo pkill -f 'nginx: worker process'
sudo systemctl start nginx
После этого снова проверьте, кто слушает порт. Если сокет принадлежит только текущему экземпляру nginx, конфликт устранён.
Отдельная рекомендация для production: не смешивайте управление через systemd и ручной запуск бинарника nginx. Такой микс часто и создаёт путаницу с процессами и сокетами.
Частый сценарий №3: Docker публикует 80 или 443 наружу
Если на сервере есть контейнеры, порт вполне может быть опубликован Docker. Особенно часто это происходит после запуска образов с параметрами -p 80:80 или -p 443:443, а также в compose-файлах с секцией ports.
Сначала снова проверяем владельца сокета:
sudo ss -ltnp '( sport = :80 or sport = :443 )'
Если видите docker-proxy, посмотрите список контейнеров и их публикации:
docker ps --format 'table {{.ID}}\t{{.Names}}\t{{.Ports}}'
Нужно найти контейнер, который публикует 0.0.0.0:80->80/tcp или 0.0.0.0:443->443/tcp. Дальше вариантов обычно три:
- остановить контейнер, если он лишний;
- сменить публикацию порта, например на
8080:80; - оставить приложение за nginx и не публиковать стандартные порты наружу.
Пример остановки контейнера:
docker stop CONTAINER_ID
В compose-конфигурации правка обычно выглядит так:
ports:
- "8080:80"
После изменения контейнеры нужно пересоздать, и только потом запускать nginx на внешних 80 и 443.
На серверах с несколькими приложениями чаще всего надёжнее держать nginx единственной точкой входа, а контейнерные сервисы слушать на внутренних портах. Если проекту нужна более гибкая сетевая схема и изоляция, удобнее размещать его на отдельном VDS, где карта портов полностью под вашим контролем.
Частый сценарий №4: systemd socket activation или чужой socket-unit
Более редкий, но неприятный сценарий — порт удерживает не сервис, а systemd-сокет. Приложение может даже не быть запущено, но порт уже зарезервирован unit-файлом типа .socket.
Проверьте активные сокеты:
sudo systemctl list-sockets --all | grep -E ':80|:443'
И дополнительно посмотрите список socket-unit:
systemctl list-unit-files | grep socket
Если выяснится, что порт держит какой-то socket-unit, останавливать и отключать нужно именно его, а не только связанный service-unit:
sudo systemctl stop example.socket
sudo systemctl disable example.socket
После этого снова выполните ss -ltnp. Это типичная ловушка: сервиса как будто нет, а порт всё равно занят.
Для обычного nginx на Debian и Ubuntu socket activation обычно не используется, но на сервере могут жить вспомогательные прокси, кастомные сервисы или вручную созданные unit-файлы.
Частый сценарий №5: Certbot, панели управления и автоматические изменения
Иногда конфликт появляется после автоматических действий: установки Certbot, панели управления, шаблона развёртывания или готового скрипта инициализации. Один компонент считает, что должен работать через Apache, другой — что через nginx.
В такой ситуации полезно проверить историю установленных пакетов и список активных сервисов:
dpkg -l | grep -E 'nginx|apache2|certbot'
sudo systemctl --type=service --state=running | grep -E 'nginx|apache2'
Если вы недавно настраивали HTTPS, внимательно проверьте, не активировался ли параллельный веб-сервер через плагин или зависимость пакета. А после восстановления работы не забудьте проверить базовые заголовки безопасности, о которых мы отдельно писали в статье про HTTP security headers в nginx и Apache.
Если проект ещё только разворачивается и нужно быстро поднять сайт с сертификатом, у Fastfox есть SSL-сертификаты для типовых веб-проектов без лишней ручной возни.

Что делать, если занят не 80, а 443
Для порта 443 логика та же самая. Разница только в типичных конкурентах: старый Apache с SSL-vhost, Docker-контейнер с HTTPS, второй reverse proxy или тестовый стенд с альтернативным веб-сервером.
Проверки остаются стандартными:
sudo ss -ltnp '( sport = :443 )'
sudo lsof -nP -iTCP:443 -sTCP:LISTEN
Дополнительно проверьте конфигурацию nginx на дубли listen 443 ssl с одинаковыми адресами и конфликтующими параметрами. На серверах с большим числом vhost-файлов проблема часто всплывает после добавления нового сайта.
Как отличить конфликт процесса от ошибки конфигурации
Снаружи симптомы похожи, но подход к решению разный. Если порт реально занят чужим процессом, ss и lsof покажут владельца сокета даже при остановленном nginx. Если же конфликт внутри конфигурации, владелец сокета может быть сам nginx или ошибка будет воспроизводиться только на этапе теста конфига.
Практический порядок такой:
- Выполнить
sudo nginx -t. - Проверить
sudo ss -ltnp '( sport = :80 or sport = :443 )'. - Если порт свободен, искать дубли
listenи ошибки в include-файлах. - Если порт занят, разбираться с конкретным процессом или socket-unit.
Такой алгоритм почти всегда приводит к ответу быстрее, чем бессистемное чтение десятков файлов в /etc/nginx.
Безопасный runbook для production
Если сервер боевой и на нём уже крутятся сайты, лучше идти по короткому регламенту, чтобы не устроить лишний простой.
- Проверить конфигурацию:
sudo nginx -t. - Посмотреть владельца портов:
sudo ss -ltnp '( sport = :80 or sport = :443 )'. - Определить, это Apache, Docker, socket-unit или второй nginx.
- Понять, нужен ли этот сервис прямо сейчас.
- Только затем останавливать лишний сервис или менять публикацию порта.
- После освобождения порта запускать nginx.
- Проверить, что сервис реально слушает сокеты и отвечает локально.
Финальная проверка:
sudo systemctl restart nginx
sudo systemctl status nginx --no-pager
sudo ss -ltnp '( sport = :80 or sport = :443 )'
curl -I localhost
curl -kI https://localhost
Профилактика: как не ловить bind failed повторно
Полностью исключить такие конфликты нельзя, но их легко сделать редкими и предсказуемыми.
- Назначьте один основной фронтенд на портах
80и443. - Не запускайте nginx вручную, если управляете им через systemd.
- Для контейнеров публикуйте наружу только действительно нужные порты.
- Храните конфиги веб-сервера и compose-файлы в Git.
- После установки новых пакетов проверяйте, какие сервисы включились автоматически.
- Документируйте карту портов для команды.
На серверах с несколькими проектами особенно полезно заранее описать, какой сервис слушает внешний трафик, какой работает только на localhost, а какой доступен лишь во внутренней сети контейнеров. Тогда ошибка bind failed превращается из аварии в рутинный кейс.
Итог
Сообщение nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) в Debian и Ubuntu почти всегда сводится к одному из нескольких сценариев: порт занят Apache, Docker-контейнером, другим экземпляром nginx или systemd-сокетом. Реже причина скрыта в дублирующейся конфигурации самого nginx.
Самый короткий путь к решению — не гадать, а сразу проверить конфиг и владельца сокета:
sudo nginx -t
sudo ss -ltnp '( sport = :80 or sport = :443 )'
sudo lsof -nP -iTCP:80 -sTCP:LISTEN
sudo lsof -nP -iTCP:443 -sTCP:LISTEN
После этого остаётся либо освободить порт, либо исправить конфигурацию. А если вы только выбираете площадку для сайтов и сервисов, у Fastfox можно купить хостинг под разные задачи — от простого сайта до изолированного серверного окружения.


