What it means
Error 98 is the Linux error EADDRINUSE: a socket is already bound to that address and port. nginx needs every listen socket to start, so it exits:
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: [emerg] still could not bind()Through systemd you first see Job for nginx.service failed because the control process exited with error code. The lines above are in journalctl -u nginx.
Common causes
1. Another web server owns the port. Apache (apache2 or httpd), Caddy or Traefik was installed alongside nginx, or came with a package. Confirm which process listens:
sudo ss -ltnp 'sport = :80'
sudo lsof -i :80 -i :4432. An nginx that systemd does not know about. Someone ran sudo nginx by hand, so systemctl start nginx collides with the master already running. The ss output then names nginx itself.
3. A Docker container publishes the port. -p 80:80 makes docker-proxy (or the container's own nginx) hold port 80 on the host:
docker ps --filter publish=804. Certbot standalone, during a renewal. certbot --standalone runs its own server on port 80 for a few seconds. An nginx restart in that window fails.
5. The config collides with itself. listen [::]:80 ipv6only=off next to a plain listen 80 opens a dual-stack socket that already covers IPv4 port 80, so nginx fails on its own second socket. ss shows nothing on port 80 while nginx is stopped. Keep listen 80 and listen [::]:80 without ipv6only=off.
How to fix
- Run
sudo ss -ltnp 'sport = :80'and note the process and its PID. - If it is another web server you do not need, stop and disable it:
sudo systemctl disable --now apache2. - If it is a stray nginx, stop it cleanly with
sudo nginx -s quit, then start it the normal way:sudo systemctl start nginx. - If it is a container, stop it or publish it on another port (
-p 8080:80) and proxy to it from nginx. - If both servers must run, move one of them to another port or another IP in its
listendirective. - Test the config and start:
sudo nginx -t && sudo systemctl start nginx.nginx -tdeliberately ignores error 98 when it tries the ports, so it passes even while another process, or a secondlistenof the same config, holds port 80. If the start still fails, go back to step 1.
Is it just you?
Check the public URL from several regions with Is my site down?: if nginx did not start, every region fails, or gets another server's page.
Get alerted next time
- An HTTP monitor on the public URL fails when nothing answers or the status code is not the one you expect.
- A TCP port monitor on ports 80 and 443 fails once nothing accepts connections there. If another server took the port and answers 200, only a keyword check on text from your own site notices.
- Each failure is rechecked from your other regions before an outage opens.
FAQ
Why does it say 0.0.0.0 and [::]? ▼
listen 80 binds every IPv4 address (0.0.0.0) and listen [::]:80 every IPv6 address. nginx tries each listen socket in turn and logs one line per failure, so you often see both lines for a single conflict.
Does listen 80 in several server blocks cause this? ▼
No. nginx opens one socket per address and port and shares it between server blocks. Duplicate server blocks give other errors, such as a duplicate default server. Error 98 usually means a separate process holds the port, but a config can also collide with itself: listen [::]:80 ipv6only=off next to listen 80 makes the IPv6 socket take IPv4 port 80 as well, so the second bind fails. Drop ipv6only=off or the plain listen 80.
What about (99: Cannot assign requested address) or (13: Permission denied)? ▼
Error 99 means the IP in the listen directive is not on this machine, often because nginx starts before the network is up at boot. Error 13 means nginx was started without root, which ports below 1024 need, or SELinux blocks the port.