You run systemctl start nginx or restart it after a change, and it fails. The status shows a line like bind to 0.0.0.0:80 failed (98: Address already in use). Only one program can listen on a port at a time, and something else got there first: often Apache, an old Nginx process, or a container. This guide finds the culprit, frees the port and gets Nginx running.
Before you start
- A Linux VPS with Nginx installed.
- SSH access with sudo. If SSH works but the website is down, that is fine; this does not affect SSH.
Confirm the error
sudo systemctl status nginx --no-pager
sudo journalctl -u nginx -n 30 --no-pagerLook for:
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:443 failed (98: Address already in use)The address and port tell you which one is taken. A related message, duplicate listen options for 0.0.0.0:80, means a problem in your own config, covered further down.
Find what is using the port
sudo ss -ltnp '( sport = :80 or sport = :443 )'-l shows listening sockets, -t TCP, -n numbers instead of names, and -p the process. Example output:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("apache2",pid=1123,fd=4),("apache2",pid=1122,fd=4))The users: part names the program and its process ID. If ss is not available, sudo lsof -i:80 gives the same answer.
Apache is running
The most common case, especially on AlmaLinux or Rocky (httpd) or after installing a package that pulled in Apache.
Ubuntu and Debian:
sudo systemctl stop apache2
sudo systemctl disable apache2AlmaLinux and Rocky Linux:
sudo systemctl stop httpd
sudo systemctl disable httpddisable stops it starting again at the next reboot.
Warning: If Apache was serving live sites, stopping it takes them offline until Nginx is configured for them. Check ls /etc/apache2/sites-enabled/ or /etc/httpd/conf.d/ first. If you want both, move Apache to another port such as 8080 and proxy to it. See Switch from Apache to Nginx.
An old Nginx process
Sometimes Nginx itself holds the port, for example a copy started by hand with sudo nginx instead of systemd. ss shows nginx as the process, but systemd thinks it is stopped.
Stop it cleanly, then start it through systemd:
sudo nginx -s quit
sudo systemctl start nginxIf nginx -s quit fails because the PID file is missing, stop the stray master process by its PID from the ss output:
sudo kill 1234Replace 1234 with the real PID. Wait a few seconds and check ss again before starting Nginx.
Other programs
- Docker containers publishing
80:80or443:443: list them withdocker ps --format '{{.Names}} {{.Ports}}', then stop the container or change its published port. - Caddy, Traefik, Lighttpd, a control panel's web server: stop and disable the service if Nginx should own the ports.
- A Node.js or Python app run as root on port 80: move it to a high port such as 3000 and put Nginx in front. See Set up Nginx as a reverse proxy.
- Certbot standalone: a renewal using
--standalonebriefly binds port 80. Wait a minute and try again, or switch renewals to the--nginxor webroot method.
Duplicate listen lines
If no other program holds the port, the clash may be inside your own config. Messages such as duplicate listen options for [::]:443 or a duplicate default server for 0.0.0.0:80 point there.
List every listen line with its file:
sudo nginx -T 2>/dev/null | grep -nE "# configuration file|listen"Rules to follow:
- Only one server block per port may have
default_server. - Options such as
ipv6only=on,reuseportorbacklog=may only appear on onelistenline per address and port. - On Ubuntu/Debian, a site linked twice into
sites-enabledunder two names loads twice. Check withls -l /etc/nginx/sites-enabled/.
Start Nginx
Once the port is free and the config is clean:
sudo nginx -t
sudo systemctl start nginx
sudo systemctl enable nginxCheck it works
systemctl is-active nginx
sudo ss -ltnp '( sport = :80 or sport = :443 )'The first command should print active, and ss should now show nginx on the ports. Then load your site:
curl -I http://example.com/Reboot later at a quiet time to confirm the right service starts on its own and the other one stays off.
Troubleshooting
- Port is free but Nginx still fails: read the full
journalctl -u nginxoutput. A different[emerg]line, such as a missing certificate file, may be the real error. - The other service comes back after a reboot: you stopped but did not disable it. Run
systemctl disablefor it. bind to [::]:80 failedonly: two server blocks bind IPv6 with conflictingipv6onlyoptions. Keep the default and remove the extra option.- Permission denied binding a custom port on AlmaLinux or Rocky: SELinux only allows web ports it knows. See Fix Nginx permission denied caused by SELinux.
Next steps
- Make config changes safely from now on: Test and reload Nginx config without downtime.
- Read what Nginx is doing: Read and manage Nginx access and error logs.
