sfw/fix
98: Address already in use high

nginx [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)

Another process already holds port 80 or 443, so nginx can't bind its listen socket and refuses to start.

What you see

nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()

What’s actually happening

systemctl start nginx fails immediately and the site is down. The log names the exact socket — 0.0.0.0:80 or [::]:443 — that's already taken. Two flavors: either a different web server (Apache/httpd, Caddy, a Node app) owns the port, or a previous nginx didn't shut down cleanly and a stale master is still listening. A reload won't fix it because there's nothing healthy to reload into.

Common causes

  • Apache/httpd is installed and running on the same box, already bound to 80/443 (extremely common after a control-panel install).
  • A stale nginx master process survived a botched stop or crash and still holds the socket.
  • Another service — a Docker container with -p 80:80, a dev server, HAProxy — grabbed the port first.
  • Both an IPv4 (0.0.0.0:80) and IPv6 ([::]:80) listen directive where one address is already in use.
  • Two nginx server blocks both trying to listen on the same port without one being marked default.

How to fix it

  1. Find what owns the portRun ss -tlnp 'sport = :80' (or sudo lsof -i :80, or netstat -tlnp | grep :80). It prints the PID and process name holding the socket. This is the whole diagnosis — everything depends on what that process turns out to be.
  2. If it's a stale nginx master, kill it and restartWhen the holder is nginx itself, the service got confused about its own state. sudo pkill -f 'nginx: master' (or kill the specific PID), confirm with ss -tlnp that :80 is free, then systemctl start nginx. Check ps afterward so you don't have orphaned workers.
  3. If it's Apache, decide who serves port 80Two web servers can't both own 80. Either stop and disable Apache (systemctl disable --now apache2 / httpd) if nginx is the real server, or — if Apache is your backend — change nginx to a different port and proxy, or bind Apache to 127.0.0.1:8080 and let nginx front it. Don't just stop Apache without disabling it, or it'll grab the port again on reboot.
  4. For a Docker/other-service conflict, free or remap the portdocker ps shows containers with 0.0.0.0:80->... published. Stop the container or change its host port (-p 8080:80). For non-Docker services, stop whatever ss named. Then start nginx.
  5. Fix duplicate listen directivesIf ss shows nginx isn't running at all yet but -t still complains about bind on start, you may have two server blocks listening on the same address:port. Mark one listen 80 default_server; and remove the duplicate bare listen.

Stop it recurring

On a fresh server pick one web server up front and systemctl disable the other so it can't reclaim the port after a reboot.

Related errors