What's happening
Nginx refuses to start, and the error log (or systemctl status nginx) shows something like this:
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()
Port 80 is already taken. Only one process can hold a port at a time, so Nginx bails out immediately.
The usual suspects:
- A previous Nginx process crashed or didn't shut down cleanly — it's still holding the port.
- Apache, Caddy, or another web server grabbed port 80 first.
- A Node.js, Python, or other app was started directly on port 80.
- A stale
/run/nginx.pidpointing to a dead process caused systemd to spin up a second Nginx instance.
Step 1 — Find what's using port 80
Don't guess. Run one of these commands first.
Using ss (recommended)
sudo ss -tlnp | grep ':80'
Sample output:
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=12345,fd=6))
The last column gives you the process name and PID — that's all you need.
Using lsof
sudo lsof -i :80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 12345 root 6u IPv4 98765 0t0 TCP *:http (LISTEN)
nginx 12346 www-data 6u IPv4 98765 0t0 TCP *:http (LISTEN)
Using fuser
sudo fuser 80/tcp
This prints just the PID(s). Add -v to see the process name alongside it:
sudo fuser -v 80/tcp
Step 2 — Fix based on what you found
Case A: Another Nginx instance is already running
The most common cause. You ended up with two Nginx workers competing for the same port — usually from running sudo nginx directly on a systemd-managed server.
Try the clean approach first:
sudo systemctl stop nginx
systemctl says it's already stopped, but the process is still alive? Kill it by PID:
sudo kill 12345
Still stuck? Force it:
sudo kill -9 12345
Then wipe any stale PID file:
sudo rm -f /run/nginx.pid
Now start Nginx:
sudo systemctl start nginx
Case B: Apache is running on port 80
Apache and Nginx both default to port 80 — they can't share it. Stop Apache first:
sudo systemctl stop apache2 # Debian/Ubuntu
sudo systemctl stop httpd # CentOS/RHEL
Not planning to use Apache on this server? Disable it so it doesn't come back after a reboot:
sudo systemctl disable apache2
Then start Nginx:
sudo systemctl start nginx
Case C: Another app (Node.js, Python, etc.) is on port 80
Take the PID from Step 1 and check what's actually running:
sudo ps -p 12345 -o comm=
Two ways forward:
- Stop that app and let Nginx sit in front of it as a reverse proxy — the standard setup for most stacks.
- Move that app to a different port (e.g. 3000 or 8000) and proxy it through Nginx.
Kill the process directly:
sudo kill 12345
Or stop it through its own service if it has one:
sudo systemctl stop myapp
Step 3 — Prevent it from happening again
Always use systemctl, never raw nginx commands
Running sudo nginx directly bypasses systemd's process tracking. That's exactly how orphan processes end up holding port 80 long after you think Nginx is stopped.
Stick to these four commands:
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl reload nginx # graceful config reload, zero downtime
sudo systemctl restart nginx
Keep Nginx enabled so systemd manages it properly
sudo systemctl enable nginx
Want to verify the full service config?
sudo systemctl cat nginx
Check that the [Service] section includes ExecStartPre=/usr/sbin/nginx -t. With this in place, a config syntax error fails fast — before Nginx even tries to bind the port.
Running both Apache and Nginx on the same server
Move Apache to port 8080 and put Nginx in front. In /etc/apache2/ports.conf:
Listen 8080
Then in your Nginx server block:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Step 4 — Verify the fix
Check that Nginx is actually up:
sudo systemctl status nginx
Look for Active: active (running). Then confirm port 80 is now owned by Nginx:
sudo ss -tlnp | grep ':80'
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=9876,fd=6))
One quick HTTP test to seal it:
curl -I http://localhost
HTTP/1.1 200 OK means the port is free and Nginx is serving. You're done.
Quick reference
# 1. Find who owns port 80
sudo ss -tlnp | grep ':80'
# 2. Stop the conflicting process (example: apache)
sudo systemctl stop apache2
# 3. Remove stale PID file if needed
sudo rm -f /run/nginx.pid
# 4. Start Nginx
sudo systemctl start nginx
# 5. Verify
sudo systemctl status nginx
curl -I http://localhost

