You just installed Nginx (or Apache) on your VPS, run systemctl start nginx, and instead of a clean start you get a wall of red: "bind() to 0.0.0.0:80 failed (98: Address already in use)". Or maybe the service was running fine for months and suddenly won't restart after a routine reboot. Either way, something else on the box already has port 80 or 443, and the web server you actually want can't grab it.
This is one of those errors that looks scary but is almost always a two-minute fix once you know where to look. Here's how to find the culprit and get your web server running again — without just rebooting and hoping.
Symptom
You'll see one of these depending on your stack:
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)(98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:443(Apache)Job for nginx.service failed because the control process exited with error code- The site loads someone else's default page, or a completely different app, instead of yours
Sometimes the service technically "starts" (systemd reports active) but silently isn't listening where you expect, because a previous worker process never let go of the socket.
Cause: Something Else Already Owns the Port
Ports 80 and 443 can only be held by one process at a time. On a fresh VPS this usually happens for one of these reasons:
| Cause | How it happens |
|---|---|
| Both Apache and Nginx installed | A LAMP script or panel installer pulled in Apache, then you installed Nginx separately — both try to bind on boot |
| A leftover process | A previous nginx -s stop or crash left a worker process running that never released the socket |
| Docker | A container published with -p 80:80 is holding the host's port 80 |
| A control panel's built-in web server | Some panels (or their default install) run their own lightweight HTTP server on 80 for the setup wizard |
| Another Nginx instance | Nginx installed both from apt and manually compiled, or two config sources loaded at once |
Fix
Step 1: Find out who actually holds the port
Don't guess — check directly. Use ss (or netstat if you still have it):
sudo ss -tulpn | grep ':80\|:443'
You'll get output like:
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("apache2",pid=1842,fd=4))
That pid=1842 and process name (apache2 here) tell you exactly what's in the way. If ss isn't installed, sudo lsof -i :80 gives the same information in a slightly different format.
Step 2: Decide what to do with it
You have three real options, depending on what you found:
If it's a service you don't need running on that port (e.g., Apache, and you're standardizing on Nginx):
sudo systemctl stop apache2
sudo systemctl disable apache2
Stopping it just frees the port for this session; disable stops it from grabbing the port again on the next reboot. Do both.
If it's a stray process with no clean service to stop (common after a crash), kill it by PID:
sudo kill 1842
# if it ignores that:
sudo kill -9 1842
Then start your intended web server again.
If it's a Docker container, check what's published on 80:
sudo docker ps --format 'table {{.Names}}\t{{.Ports}}'
Either stop that container (docker stop <name>) or, if you need both running, remap the container to a different host port (e.g. -p 8080:80) and put Nginx in front of it as a reverse proxy instead of fighting over the same port.
Step 3: Confirm the fix
sudo systemctl restart nginx
sudo systemctl status nginx
sudo ss -tulpn | grep ':80\|:443'
You should now see nginx — not some other process — listed against 80 and 443, and systemctl status should show active (running) with no bind errors in the log.
If it's Nginx fighting Nginx
Occasionally the "other" process holding the port is another Nginx instance — for example one installed via apt and another compiled from source, or a leftover master process from before a config reload. Run:
ps aux | grep nginx
If you see multiple nginx: master process entries with different binary paths, stop all of them cleanly, confirm nothing is listening (ss -tulpn | grep :80 should return nothing), then start the one you actually want.
Prevention
- Pick one web server per port and stick with it. If you're moving from Apache to Nginx (or vice versa), fully remove or disable the old one instead of leaving it installed "just in case."
- Mask services you never want auto-starting:
sudo systemctl mask apache2is stronger thandisable— it prevents anything (including another package) from starting it even indirectly. - Use a reverse proxy pattern for Docker apps. Bind containers to high ports (8080, 8443, etc.) and let Nginx on the host handle 80/443, forwarding to the container. This avoids the "who owns port 80" fight entirely and gives you TLS termination in one place.
- Check before you reboot after config changes. Run
sudo nginx -t(orapachectl configtest) before restarting, so a bad config doesn't leave the service down and something else able to grab the port in the meantime.
Frequently Asked Questions
Can two web servers share port 80 if they're on different domains?
No. Port binding happens at the IP:port level, not the domain level — the OS doesn't know about domains yet at that point. If you need multiple sites, run one web server on port 80 and use virtual hosts (Apache) or server blocks (Nginx) to route by hostname, or put Nginx in front as a reverse proxy for other backends.
Why does this happen right after a server reboot?
Systemd starts enabled services in whatever order their unit files and dependencies resolve to, which isn't always the order you expect. If Apache and Nginx are both enabled, whichever starts first wins the port, and the second one fails. Disabling (or masking) the one you don't want fixes this permanently.
I killed the process but the port still shows as in use — why?
Sockets in TIME_WAIT state can briefly appear "in use" after a process exits, especially for connections that were mid-transfer. Wait 30-60 seconds and check again with ss -tulpn. If it's still stuck after that, check for a parent process (like a supervisor or Docker daemon) that respawned it.
Does this affect FTP or SSH too?
The same underlying problem (two services fighting over one port) can happen on any port, including 21 (FTP) or 22 (SSH), but it's rarer since those usually only run one service by default. The diagnosis steps are identical: find the PID with ss -tulpn, decide what should own it, stop or reconfigure the other one.
Is it safe to just change my web server to a different port instead?
Technically yes, but it's a workaround, not a fix — visitors expect websites on 80/443, and running on a non-standard port means appending :8080 to every URL. Only do this intentionally for internal or staging services, not for a public-facing site.
