Fix "Address Already in Use" on Linux (Port Conflicts)

You start your service and it dies before it ever serves a request:

code

$ ./myserver
error: listen tcp 0.0.0.0:8080: bind: address already in use

Node calls this EADDRINUSE, Python raises OSError: [Errno 98], Go prints the line above. Same kernel error underneath: bind(2) returned EADDRINUSE.

What the error actually means #

A listening socket is a tuple: protocol, local address, local port. The kernel lets exactly one socket own a given host:port combination for a protocol at a time. When your process asks to bind() to 0.0.0.0:8080 and something already holds that tuple, the kernel refuses. That is the whole story. It is not a firewall, not DNS, not permissions (usually). Something is already listening, or a recently closed socket is still holding the port.

Two questions get you to a fix: what owns the port, and do you want that thing gone or do you want a different port.

Find the culprit #

ss is the modern tool. -l listening, -t TCP, -n numeric (skip name resolution), -p show the process:

bash.bash

$ sudo ss -ltnp 'sport = :8080'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 511 0.0.0.0:8080 0.0.0.0:* users:(("node",pid=4821,fd=23))

There it is: PID 4821, an old node process still holding 8080. Run ss with sudo, otherwise the Process column comes back empty for sockets you do not own.

lsof gives the same answer with a different lens and is handy when you want the full command line and user:

bash.bash

$ sudo lsof -nP -i :8080
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 4821 kiril 23u IPv4 512377 0t0 TCP *:8080 (LISTEN)

fuser is the quickest when you just want the PID (and optionally to nuke it):

bash.bash

$ sudo fuser 8080/tcp
8080/tcp: 4821

Kill it or move #

If the process is a stale copy of your own service, kill it:

bash.bash

$ sudo kill 4821 # SIGTERM, lets it clean up
$ sudo kill -9 4821 # SIGKILL, only if it ignores the first

fuser -k 8080/tcp does the lookup and the kill in one shot. Be deliberate here: on a shared box that PID might be someone else's database. Confirm the command name before you signal anything.

If the port legitimately belongs to another service, do not fight it. Change your own port instead. Most servers take a flag or an env var:

bash.bash

$ PORT=8081 ./myserver
$ ./myserver --listen 127.0.0.1:8081

The TIME_WAIT case: your just-stopped server can't rebind #

This one confuses people. You stop your server, immediately restart it, and get address already in use even though ss shows nothing listening. What you will see instead is a socket in TIME_WAIT:

bash.bash

$ ss -tan | grep 8080
TIME-WAIT 0 0 10.0.1.5:8080 10.0.1.9:52344

When a TCP connection closes, the side that closed first keeps the socket in TIME_WAIT for up to twice the maximum segment lifetime (commonly ~60s on Linux). This is deliberate: it lets any straggling packets from the old connection die out so they can't be mistaken for a new one. During that window the kernel, by default, refuses to bind a fresh listening socket to that same local address and port.

The wrong fix is to wait, or to lower tcp_fin_timeout. The right fix lives in your code: set SO_REUSEADDR on the listening socket before you bind. It tells the kernel a TIME_WAIT socket on that address is fine to reuse.

python.python

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # before bind()
s.bind(("0.0.0.0", 8080))
s.listen()

Most frameworks set this for you. Python's http.server with allow_reuse_address = True, Go's net.Listen, and Node's net all enable it by default, which is why you rarely hit TIME_WAIT in those. If you wrote the socket code by hand and skipped it, this is your bug.

A related option, SO_REUSEPORT, is different: it lets multiple sockets bind the same port at once so several worker processes can each accept connections, with the kernel load-balancing across them. Reach for it when you want that behavior, not as a TIME_WAIT workaround.

0.0.0.0 vs 127.0.0.1 conflicts #

Address matters as much as port. 0.0.0.0 means "every interface on this host." 127.0.0.1 means loopback only. A socket bound to 0.0.0.0:8080 covers 127.0.0.1:8080 too, so a second process trying 127.0.0.1:8080 collides with it. The reverse can also surprise you: without SO_REUSEADDR, you generally cannot have one process on 127.0.0.1:8080 and another on 0.0.0.0:8080. When ss shows 0.0.0.0:8080 and you thought you only bound loopback, that wildcard bind is the thing in your way.

Privileged ports (below 1024) #

Binding a port under 1024 needs privilege. Try to run a web server on 80 as a normal user and you get:

code

bind: permission denied

That is EACCES, not EADDRINUSE, but it lands in the same "won't start on this port" bucket. Rather than run the whole service as root, grant just the bind capability to the binary:

bash.bash

$ sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/myserver
$ getcap /usr/local/bin/myserver
/usr/local/bin/myserver cap_net_bind_service=ep

Now it can bind 80 or 443 while running as an unprivileged user. For interpreted apps, set the capability on the interpreter or, cleaner, listen high (8080) and let a reverse proxy or an iptables redirect handle 80.

The Docker cousin: "port is already allocated" #

Docker has its own flavor of this:

code

$ docker run -p 8080:80 nginx
docker: Error response from daemon: driver failed programming external
connectivity on endpoint: Bind for 0.0.0.0:8080 failed: port is already allocated.

Same root cause, one layer up: dockerd (or another container's published port) already holds the host side. The lookup is the same ss -ltnp on the host, plus docker ps to spot the container publishing 8080. It's common enough that it gets its own writeup: Docker port already allocated.

The call we'd make #

Run sudo ss -ltnp 'sport = :PORT' first, every time. It answers "what owns it" in one line. If the owner is a stale copy of your own service, kill it and restart. If it belongs to something real, move your service to another port instead of fighting for it. If ss shows nothing listening but you still can't bind, check for TIME_WAIT and fix it properly by setting SO_REUSEADDR in the code rather than sleeping through the timer. And when the port is below 1024, grant cap_net_bind_service instead of reaching for root. Ninety percent of these are a forgotten background process, and ss finds it in seconds.

For the broader toolkit, see our Linux troubleshooting guide.