LearnDevOpsHands-On Labs
Troubleshooting Matrix/bind: address already in use
LinuxHigh Severity

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.

Observed Error Signature / Log Pattern
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

BASHDiagnostic / remediation command
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.

BASHDiagnostic / remediation command
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.

BASHDiagnostic / remediation command
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.

BASHDiagnostic / remediation command
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.

BASHDiagnostic / remediation command
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.

BASHDiagnostic / remediation command
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.

BASHDiagnostic / remediation command
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.

BASHDiagnostic / remediation command
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.

BASHDiagnostic / remediation command
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.

Open Linux Sandbox