bind: address already in use
Fix "bind: address already in use": run ss -ltnp or lsof -i :PORT to find the listener, then stop it. For TIME_WAIT, set SO_REUSEADDR. Covers IPv6 overlap.
bind: address already in use
Incident Overview
Fix "bind: address already in use": run ss -ltnp or lsof -i :PORT to find the listener, then stop it. For TIME_WAIT, set SO_REUSEADDR. Covers IPv6 overlap.
easy fix·6 min read
By Vishvesh Patel·DevOps Engineer·Updated 21 September 2026
bind: address already in use Error: listen EADDRINUSE: address already in use :::3000 nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) OSError: [Errno 98] Address already in use
Do this first3 steps
Run these in order. Each one tells you what its output means before you change anything.
• Find what holds the port
The users: field names the process and PID. Run it with sudo, because without it ss hides the process name for sockets owned by other users and the output looks empty of useful detail.
• If nothing owns it, check for sockets still closing
TIME-WAIT or FIN-WAIT entries with no listener mean the kernel is still holding the address from a previous process. This clears on its own within about a minute, and SO_REUSEADDR avoids the wait.
• Stop the process properly rather than killing it
For anything under systemd, killing the PID invites the supervisor to restart it straight away, so the port is taken again before you can bind. Stop the unit instead.
EADDRINUSE has two distinct causes and the fix differs:
One command separates them.
sudo ss -tulpn 'sport = :3000'
Find the listener
The sudo matters: without it ss will not show the process name for sockets owned by other users, and you get a confusing result that looks like nothing is there.
lsof is the alternative if you prefer it:
On older systems netstat -tulpn | grep :3000 still works; ss is the modern replacement and is significantly faster on a busy host.
sudo ss -tulpn 'sport = :3000'
Case 1: Something is listening
Identify it properly before you kill anything:
PPID 1 means it is supervised by systemd or was orphaned. If it is a systemd service, stop the unit rather than the process, or the supervisor restarts it immediately and the port is taken again before you can bind:
For something genuinely stray:
kill -9 gives the process no chance to close its listening socket or flush anything, which is how you end up with the TIME_WAIT situation below.
Avoid fuser -k 3000/tcp and kill $(lsof -t -i:3000) as reflexes. Both kill whatever is there without you having read what it is, which in a shared environment is how the wrong thing gets killed.
ps -p 48213 -o pid,ppid,user,etime,cmd
Case 2: Nothing is listening
The old process is gone and its connections are still winding down. TCP keeps the pair in TIME_WAIT for twice the maximum segment lifetime, 60 seconds on Linux, so that late packets from the old connection are not delivered to a new one.
Wait a minute and it clears. Or, better, make your server able to bind anyway.
sudo ss -tulpn 'sport = :3000' # no output ss -tan 'sport = :3000' | head
SO_REUSEADDR is the real fix
A server should set SO_REUSEADDR before binding. It permits binding to a local address that has connections in TIME_WAIT, which is exactly the "I just restarted my service" case.
Most frameworks do this for you. Some do not:
This does not let two processes listen on the same port. It only allows binding over lingering TIME_WAIT entries. SO_REUSEPORT is the different option that genuinely allows multiple listeners, used deliberately for load balancing across worker processes.
Do not reach for net.ipv4.tcp_tw_reuse. It is a client-side setting for outgoing connections, it does not help a listener, and tcp_tw_recycle, which people often mean, was removed from the kernel in 4.12 because it broke connections from behind NAT.
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("0.0.0.0", 3000))The IPv4 and IPv6 overlap
The :::3000 is IPv6 [::]:3000. By default a socket bound to [::] also accepts IPv4 connections through the dual-stack mapping, so an IPv6 wildcard bind occupies the IPv4 port too. A second process binding 0.0.0.0:3000 then collides with something that does not appear when you only look at IPv4.
Check both before concluding nothing is there.
Error: listen EADDRINUSE: address already in use :::3000
Ports below 1024
If binding to 80 or 443 fails with permission denied rather than address in use, that is a different problem: privileged ports need CAP_NET_BIND_SERVICE. For a systemd service:
Worth knowing because the two errors get conflated, and running the whole service as root to work around it is a poor trade.
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
In containers
Inside a container the port is namespaced, so a conflict is usually on the host side of a published port:
Let Docker choose a host port when you do not care:
Two containers can both listen on 3000 internally without any conflict, as long as they publish to different host ports or none at all.
In a Kubernetes Pod, containers share a network namespace, so two containers in the same Pod listening on the same port do conflict. hostPort and hostNetwork reintroduce host-level conflicts and are worth avoiding for that reason among others.
docker run -p 3000:3000 myapp # 3000 on the host must be free
A checklist
Frequently Asked Questions
How do I find which process is using a port on Linux?
sudo ss -tulpn 'sport = :3000', and the sudo is not optional: without it ss omits the process name for sockets owned by other users, so the output looks unhelpfully empty. sudo lsof -i :3000 -sTCP:LISTEN gives the same answer. On older systems netstat -tulpn | grep :3000 still works, though ss is the modern replacement and is much faster on a host with many connections. Follow up with ps -p <pid> -o pid,ppid,user,etime,cmd before you act on it.
Why is the port in use when nothing is listening?
Connections from the previous process are in TIME_WAIT. TCP holds the address and port pair for twice the maximum segment lifetime, 60 seconds on Linux, so that delayed packets from the old connection cannot be delivered to a new one that happens to reuse the same pair. ss -tan 'sport = :3000' shows the entries. It clears on its own, and the proper fix is for the server to set SO_REUSEADDR before binding, which is exactly what that option is for.
Does SO_REUSEADDR let two processes share a port?
No. It allows a bind to succeed when the address has connections lingering in TIME_WAIT, which is the restart case. Two processes cannot both hold a listening socket on the same address and port with it. The option that genuinely permits that is SO_REUSEPORT, which is a deliberate feature for spreading accepted connections across several worker processes, and it requires every listener to set it. Confusing the two leads people to expect a behaviour SO_REUSEADDR was never meant to provide.
Should I use tcp_tw_reuse to get rid of TIME_WAIT?
No. net.ipv4.tcp_tw_reuse applies to outgoing client connections and does nothing for a listening socket, so it will not fix this error. The setting people usually mean is tcp_tw_recycle, which was removed from the Linux kernel in 4.12 because it broke connections from clients behind NAT in ways that were very hard to diagnose. TIME_WAIT exists for a reason; SO_REUSEADDR on the server is the supported way to restart without waiting for it.
What does ":::3000" mean in the error message?
It is the IPv6 wildcard address [::] with port 3000. A socket bound there accepts IPv4 connections as well through dual-stack mapping, unless IPV6_V6ONLY is set, so an IPv6 wildcard bind occupies the IPv4 port too. A process then trying to bind 0.0.0.0:3000 collides with something that does not appear if you only inspect IPv4. Always look at both families, which sudo ss -tulpn | grep :3000 does by default.
Reference and practice
Learn the underlying concept
Other Linux errors
Free, comprehensive DevOps tutorials and learning roadmaps. Master Docker, Kubernetes, CI/CD, and more.
Get notified about new tutorials and features.
• Roadmaps
• Interview Prep
• Certifications
• Company
© 2026 DevOpsLesson. All rights reserved.
Need to practice this scenario?
Simulate this error condition and practice recovery commands safely in our in-browser terminal.