Linux and Bash for Developers · Processes and Resources
Address Already in Use
Find which process owns a port, stop it safely, and understand why a restarted server can't bind immediately.
Scenario
You restart a service and it crashes at startup with OSError: [Errno 98] Address already in use. The old process seems gone. Someone runs kill -9 on every Python process on the box.
What you see in production
- A server fails to start with "Address already in use" (EADDRINUSE).
- A previous instance, a forgotten dev server, or another service is listening on the port.
- Restarting the same server quickly sometimes fails and sometimes works.
The naive code
python3 app.py & # started by hand last week and forgotten
systemctl restart app # the real service now can't bind port 8080
pkill -9 python # "fix": kills every Python process on the machine
Reproduce it
python3 -m http.server 18431 --bind 127.0.0.1 >/dev/null 2>&1 &
first=$!
sleep 0.5
python3 -m http.server 18431 --bind 127.0.0.1 2>&1 | tail -n 1
kill $first; wait $first 2>/dev/null || true
Output
OSError: [Errno 98] Address already in use
Run this on your computer — see “Get Started” for setup.
Root cause
Only one socket can listen on an address and port unless both opt into sharing. Some other process (often an old instance) already holds it. Separately, a closed server's connections can linger in TIME_WAIT; servers that don't set SO_REUSEADDR may fail to rebind for a short time after a restart.
The fix
Find the owner first, then stop exactly that process, gracefully:
python3 -m http.server 18432 --bind 127.0.0.1 >/dev/null 2>&1 &
sleep 0.5
ss -ltnp 'sport = :18432' | awk 'NR > 1 { print "listening:", $4, $6 }' | sed 's/,fd=[0-9]*//'
pid=$(ss -ltnp 'sport = :18432' | grep -o 'pid=[0-9]*' | head -n 1 | cut -d= -f2)
ps -o pid=,args= -p "$pid" | sed 's/^ *[0-9]* /process: /'
kill -TERM "$pid"
sleep 0.5
ss -ltn 'sport = :18432' | grep -q LISTEN || echo "port 18432 is free"
Output
listening: 127.0.0.1:18432 users:(("python3",pid=3921236))
process: python3 -m http.server 18432 --bind 127.0.0.1
port 18432 is freeRun this on your computer — see “Get Started” for setup.
ss -ltnp(orlsof -i :PORT) shows the listening process;-pneeds root for other users' processes.- Stop it with
SIGTERM(orsystemctl stopif it's a service), and only useSIGKILLif it won't exit. - Run services under a supervisor (systemd, Docker) so there's exactly one managed instance.
- Servers should set
SO_REUSEADDR(most frameworks do) so restarts don't fail onTIME_WAIT.
Trade-offs
pkill -9 pythonmay kill unrelated processes and skips cleanup.- Changing the port works around the conflict but leaves a rogue process running.
SO_REUSEPORTlets several processes share a port for load balancing; don't confuse it with fixing conflicts.
Detect it in production
- Service logs show EADDRINUSE at startup; alert on crash loops.
- Inventory listening ports regularly (
ss -ltn) and compare against what should be running. - In containers, map ports explicitly and check
docker psfor duplicates.
Key points
- Find the owner with ss or lsof before killing anything.
- Stop processes gracefully, and let a supervisor own the service.
- Know the difference between a real conflict and TIME_WAIT on restart.
Further reading: ss(8) · socket(7): SO_REUSEADDR
Practice questions
How do you find which process is listening on port 8080?
Answer
ss -ltnp 'sport = :8080' (or lsof -i :8080); root is needed to see other users' processes.
Why might a server fail to bind right after a restart even though the old process exited?
Answer
Old connections can linger in TIME_WAIT; servers that don't set SO_REUSEADDR can't rebind until they expire.
Why avoid pkill -9 to free a port?
Answer
It can kill unrelated processes and gives none of them a chance to clean up; find the owner and stop it with SIGTERM first.
Open in the app to mark lessons complete, take notes and track your progress.