Linux Error: bind: Address already in use — Cause, Fix, and Troubleshooting Guide
How to fix bind: Address already in use (EADDRINUSE) on Linux. Find the listener with ss and lsof, clear TIME_WAIT, and untangle systemd socket conflicts.
- #linux
- #troubleshooting
- #errors
- #networking
Summary
bind: Address already in use (errno EADDRINUSE) is returned when a process calls bind() on an IP/port pair the kernel already considers claimed. The socket layer refuses to let two endpoints hold the same address tuple, so the second binder fails and the service dies on launch. It is one of the most common reasons a daemon exits immediately on start. The conflict is per address tuple, so 127.0.0.1:8080 and 0.0.0.0:8080 collide, while 10.0.0.5:8080 and 10.0.0.6:8080 do not.
Common Symptoms
- A service exits immediately on start with
Address already in use/EADDRINUSE. nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)in the logs.systemctl statusshows the unitfailed (Result: exit-code)right afterstart.- A restart fails, but a wait-and-retry sometimes succeeds (TIME_WAIT clearing).
- Two copies of the daemon appear briefly, or an old PID still owns the port.
Most Likely Causes of the ‘bind: Address already in use’ Error
Most production bind: Address already in use failures come from these, roughly in order of frequency:
- Another process already listens on the port — an unrelated service (or an earlier deploy) holds the tuple.
- The same service started twice — a double
start, a supervisor relaunch that did not kill the old worker, or anExecReloadrace left a prior instance bound. - Socket stuck in
TIME_WAITwith noSO_REUSEADDR— after a close, the listening tuple lingers and an immediate restart fails until the timer expires. - A previous instance not fully terminated — a killed-but-not-reaped process or a child that inherited the listening socket keeps the port claimed (often re-parented to
PPID 1). - A
0.0.0.0wildcard bind overlapping a specific-IP bind —0.0.0.0:443conflicts with10.0.0.5:443on the same port, in either direction. - A systemd socket-activation
.socketunit already holding the port, colliding with the daemon’s ownbind().
Quick Triage
# Who owns the port right now? (PID, command, bound address)
ss -ltnp 'sport = :8080'
# Is it a live listener or just stale connections?
ss -tan 'sport = :8080' | head
# Anything holding the socket that ss missed?
sudo fuser -v 8080/tcp
A LISTEN row means a live owner; only TIME-WAIT/CLOSE-WAIT rows mean a stale tuple that will clear on its own.
Diagnostic Commands
# Authoritative owner + bound local address (specific IP vs 0.0.0.0)
ss -ltnp 'sport = :<PORT>'
sudo lsof -iTCP:<PORT> -sTCP:LISTEN -P -n
ss/lsof name the PID, command, and the exact local address bound — note whether it is a specific IP or the wildcard.
# Duplicate instances of your own service
pgrep -a <service>
ps -o pid,ppid,stat,cmd -p "$(fuser <PORT>/tcp 2>/dev/null)"
Two PIDs of the same service, or a re-parented orphan (PPID 1), points at a double-start or unclean shutdown.
# systemd socket-activation conflicts
systemctl list-sockets | grep ':<PORT>'
systemctl status <service>.socket 2>/dev/null
If a .socket unit owns the port, the daemon should not also bind it. ss will show users:(("systemd",pid=1,...)) when systemd holds the socket. On Ubuntu/Debian and RHEL/Rocky the commands are identical; only the unit file paths (/lib/systemd/system vs /usr/lib/systemd/system) differ.
Fix / Remediation
- Confirm the owner and its bound address with
ss -ltnp 'sport = :<PORT>'. Decide whether the claim is legitimate, stale, or your own duplicate. - If it is a stale
TIME_WAITtuple, simply retry after a few seconds, or enableSO_REUSEADDRon the listening socket in the app — nearly every server framework exposes this. The kernel clearsTIME_WAITon its own. - If a rightful service owns the port, reconfigure your daemon to a free port, or stop the existing service cleanly with
systemctl stop <service>. - If a
.socketunit holds it,systemctl stop/disablethe conflicting socket unit, or let the activated service own the socket instead of binding it again. - If it is a stray or orphaned copy of your own service, kill it, then restart through the manager.
Warning:
fuser -kandkillterminate whatever holds the socket, including a legitimate production listener. Confirm the PID and command first —fuser -kis a last resort.
sudo fuser -k <PORT>/tcp # last resort: kill the socket holder
sudo systemctl restart <service>
Validation
# Exactly one LISTEN on the expected address, no stray TIME_WAIT pileup
ss -ltnp 'sport = :<PORT>'
ss -tan 'sport = :<PORT>' | head
# Service is active and stayed up
systemctl status <service> --no-pager
One LISTEN row on the intended local address, and a unit that stays active (running), confirms the port is cleanly claimed.
Prevention
- Enable
SO_REUSEADDRon listening sockets so a fast restart over aTIME_WAITtuple does not fail. - Set
KillMode=mixed(orcontrol-group) and a definiteExecStopin systemd units so no orphaned child survives to squat the port after a stop or failed restart. - Standardize port assignments in config management (Ansible/Puppet) and add a pre-start
ss -ltnpcheck to catch collisions before launch. - Decide deliberately between socket-activation (
.socketunits) and in-app binding for a given port — never both. - Bind to specific addresses consistently across a host so a
0.0.0.0listener and a specific-IP listener never silently overlap. - Monitor for unexpected listeners and alert on services flapping in a restart loop.
Related Errors
- Linux Error: Address already in use
- Linux Error: Cannot assign requested address
- Linux Error: Connection refused
- Linux Error: Too many open files
- Linux Error: start request repeated too quickly
- All Linux error guides
Final Notes
bind: Address already in use means the kernel already considers your IP/port tuple claimed. Work from ss -ltnp to the owning PID and its bound address before touching anything — once you know whether the claim is live, stale, or your own duplicate, the remedy is immediate: stop the rightful listener, wait out a stale TIME_WAIT, or kill a stray orphan.
Want faster Linux incident response? Use DevOps AI Toolkit to turn production errors into clear diagnostics, remediation steps, and reusable runbooks.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.