Fix Nginx bind() to 0.0.0.0:80 Failed (Address Already in Use)
Fix nginx bind() to 0.0.0.0:80 failed (98: Address already in use) on a Django server: find the process holding port 80 or 443, stop Apache or a stray Nginx, and fix duplicate listen directives and Docker port clashes.
Problem statement
Nginx will not start:
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()
Another process already holds port 80 (or 443), so Nginx cannot listen there. On a fresh server this is usually Apache, installed by default on some images or pulled in as a dependency. On an existing server it is often an Nginx master that systemd lost track of, a Docker container publishing port 80, or Caddy or Traefik left over from an earlier attempt.
- Find the owner:
sudo ss -ltnp 'sport = :80'(and:443). - If it is Apache:
sudo systemctl disable --now apache2(orhttpd). - If it is an orphaned Nginx:
sudo pkill nginxthensudo systemctl start nginx. - If it is Docker:
docker ps --filter publish=80and remove or remap that container. - If no other process holds it, look for two
listenlines for the same address in your Nginx config.
Step-by-step solution
1. Identify the process
sudo ss -ltnp 'sport = :80'
sudo ss -ltnp 'sport = :443'
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("apache2",pid=812,fd=4))
The users: column names the program and PID. On older systems sudo lsof -i :80 gives the same information.
2. Apache is running
Ubuntu and Debian images with a "LAMP" history, or packages such as php and some monitoring tools, install Apache and start it on port 80:
sudo systemctl disable --now apache2
sudo systemctl start nginx
If you do not need Apache at all, remove it: sudo apt purge apache2. On RHEL-family systems the service is httpd. If you deliberately run Apache with mod_wsgi for Django, you do not also need Nginx on the same port; choose one.
3. An orphaned Nginx process
If ss shows nginx but systemctl status nginx says inactive or failed, an Nginx master was started outside systemd (for example by running sudo nginx by hand) and systemd cannot manage it:
ps -ef | grep [n]ginx
sudo nginx -s quit # graceful stop of the unmanaged master
sudo systemctl start nginx
From then on, only use systemctl to start and stop it.
4. A Docker container publishes the port
docker ps --format '{{.Names}}\t{{.Ports}}' | grep -E ':80->|:443->'
If a Compose stack publishes 80:80 for its own Nginx or Traefik, either let that container be the public proxy and stop host Nginx, or change the mapping (8080:80) and proxy to it from host Nginx. Also check for containers set to restart: always that come back after you stop them.
5. Duplicate listen directives in your config
If nothing else holds the port and Nginx still fails on start, the configuration may ask to bind the same socket twice with conflicting options, for example listen 80; in one block and listen 0.0.0.0:80 reuseport; in another, or listen [::]:80 ipv6only=off; alongside listen 80;. Review every listen line:
sudo nginx -T 2>/dev/null | grep -n "listen"
Options such as default_server, reuseport, and ipv6only may appear on only one listen per address and port.
Verification check:
sudo ss -ltnp 'sport = :80'
sudo systemctl is-enabled nginx apache2 2>/dev/null
curl -sI http://localhost | head -1
Port 80 should belong to nginx, Apache should be disabled or absent, and a reboot should bring up Nginx alone.
Explanation
A TCP listening socket is unique per address and port. When Nginx starts, the master process binds every address in its listen directives before starting workers; if any bind fails, it retries a few times ("still could not bind") and exits. Reloads do not hit this error because the master already holds the sockets and passes them to new workers, which is why the error appears at boot or on restart but not on reload.
Edge cases / notes
- Ports below 1024 need root or
CAP_NET_BIND_SERVICE. Nginx's master runs as root for this reason; a permission error (13: Permission denied) is a different problem. - IPv6 and IPv4. On Linux,
listen [::]:80withipv6only=offalso takes IPv4 port 80, which conflicts with a separatelisten 80. The defaultipv6only=onavoids that. - Snap or alternative installs of Nginx can run alongside the distribution package. Check
snap listandwhich -a nginx. - Control panels such as Plesk or cPanel manage their own web server on 80 and 443; do not install a second Nginx alongside them.
Next steps
- The same error for Gunicorn's port has its own guide: Fix Gunicorn Address Already in Use.
- For other startup failures, see Fix Common nginx emerg Errors in a Django Config.
FAQ
How do I see what is using port 80?
Run sudo ss -ltnp 'sport = :80'. The users: field shows the process name and PID.
Can Nginx and Apache both run on one server?
Yes, on different ports, for example Apache on 8080 behind Nginx. They cannot both listen on 80.
Why does reload work but restart fail?
Reload keeps the existing listening sockets. Restart releases and rebinds them, which fails if another process took the port.
Is this error related to Gunicorn?
Not directly. Gunicorn listens on its own socket or port, not on 80, unless misconfigured.