You start a web server, a database, a container or a development tool and it stops straight away, complaining that the port it wants is already taken. Typical wording:
# Linux and most Unix-like programs
bind() to 0.0.0.0:80 failed (98: Address already in use)
OSError: [Errno 98] Address already in use
# Windows
Only one usage of each socket address (protocol/network address/port) is normally permitted
Port 80 is already in use
Only one program can listen on a given address and port at a time. You need to find which one has it, decide whether it should be there, and then stop it or move your program to a different port.
Applies to: Windows 10, Windows 11 and Windows Server (PowerShell and Command Prompt, elevated for most steps), and Linux with iproute2, lsof and psmisc. Any command that stops something is in a warning box. Read the whole of “Before you stop anything” first.
What is going on
A server program asks the operating system to bind a port (an address plus a number, for example 0.0.0.0:80 or 127.0.0.1:8080) and listen on it. If something else already holds it, the second bind fails. On Windows, Microsoft describes this error (WSAEADDRINUSE, 10048) as typically only one usage of each socket address being permitted, and it can also be caused by a socket that was not closed properly or one that is still in the process of closing.
The owner is usually one of these:
- A service or daemon that starts at boot: IIS, Apache, nginx, a database, a remote-desktop or file-sharing component, a previous copy of the program you are trying to start.
- A leftover process: an earlier run that did not exit, a crashed tool, a dev server in another terminal.
- The operating system itself, which on Windows can hold a port without any ordinary program showing up (HTTP.sys, or a reserved port range).
- A container that publishes the port.
Diagnosis
Before you stop anything
Identify first, kill second. The PID you find may be a system component or a service hosting several others. Stopping the wrong one can take down networking, authentication, a database or the machine. If you cannot name the program, find out what it is before stopping it. On a production server, stop a service through its own service controls and with a change window, not by killing a process.
1. Windows: find the process that owns the port
Open an elevated PowerShell or Command Prompt. netstat -ano is the classic route. -a shows all connections and listening ports, -n shows numbers rather than names, and -o adds the process ID (PID) of each. (-b shows the executable name but, per Microsoft, can be time-consuming and fails without enough permissions.)
netstat -ano | findstr ":80 "
netstat -ano -p tcp
Look for a row with State LISTENING and the port you want in the Local Address column; the PID is the last column. The trailing space in ":80 " avoids also matching ports such as 8080 and 8000.
The PowerShell way gives you objects instead of text. Get-NetTCPConnection takes -LocalPort, -State and -OwningProcess; Get-NetUDPEndpoint takes -LocalPort and -OwningProcess:
# TCP listener on port 80
Get-NetTCPConnection -LocalPort 80 -State Listen | Select-Object LocalAddress, LocalPort, OwningProcess
# UDP port (UDP has no connection state)
Get-NetUDPEndpoint -LocalPort 53 | Select-Object LocalAddress, LocalPort, OwningProcess
# turn the PID into a process name and path
Get-Process -Id (Get-NetTCPConnection -LocalPort 80 -State Listen).OwningProcess |
Select-Object Id, ProcessName, Path
If you only have a Command Prompt, tasklist takes a filter on the PID. /svc adds the services hosted in each process:
tasklist /fi "PID eq 4321"
tasklist /svc /fi "PID eq 4321"
Replace 4321 with the PID from netstat. Microsoft documents PID as a valid /fi filter name and /svc as listing all the service information for each process.
2. Windows: is the process a service?
If the process is svchost.exe, w3wp.exe, sqlservr.exe or any other long-running program, it is probably a service. The Win32_Service WMI class has a ProcessId property, so you can ask which services run in that process:
Get-CimInstance Win32_Service -Filter "ProcessId=4321" |
Select-Object Name, DisplayName, State, StartMode, PathName
More than one row means a shared host process (a shared svchost.exe). Stopping that process would stop all of those services, so stop only the one service that matters. sc.exe queryex <servicename> is another way to get a service’s details. [UNCONFIRMED: I could not find a Microsoft Learn page for sc queryex (the page for sc query does not mention a PID field), so the PowerShell method above is the one confirmed against the documentation.]
3. Windows: nothing obvious is using the port
If netstat shows no listener on the port, or the owner is PID 4 (shown as “System”), the port is not held by an ordinary program. [UNCONFIRMED: that PID 4 is the System process which hosts HTTP.sys is not stated on the Microsoft Learn pages I used.] Two things to check:
HTTP.sys. HTTP.sys is the kernel-mode HTTP service in Windows. Components that use it, IIS being the best known, register URLs on a port rather than each opening their own listener. [UNCONFIRMED: which other products use HTTP.sys was not checked against Microsoft documentation.] Microsoft documents netsh http show servicestate (a snapshot of the HTTP Service, with view=session|requestq|client and verbose=yes|no) and netsh http show urlacl (reserved URLs):
netsh http show servicestate view=requestq
netsh http show urlacl
The request queue view is meant to show which programs have URLs registered on the port even though netstat only shows the system. [UNCONFIRMED: The Microsoft page describes the views only briefly, so what each field shows (for example the owning process IDs) is not confirmed here; read the output on your own machine.] A reserved URL (urlacl) shows which account may register that URL; it does not mean a program is running.
Excluded port ranges. Windows can reserve ranges of ports so that no application can bind them. Microsoft documents netsh interface ipv4 show excludedportrange protocol=tcp|udp as showing all excluded port ranges:
netsh interface ipv4 show excludedportrange protocol=tcp
netsh interface ipv4 show excludedportrange protocol=udp
If your port sits inside one of the listed start-end ranges, no program is “using” it; the system has set it aside, and you will not find a PID. A program that tries to bind it can fail with WSAEACCES (10013, “Permission denied”, where Microsoft says another application, service or kernel-mode driver may be bound to the same address with exclusive access) instead of 10048. The simplest fix is to use a different port. Common reservers include Hyper-V and the Windows NAT used by container tools. [UNCONFIRMED: which Windows features create these reservations on a given version was not verified against Microsoft documentation, and I have not covered how to change them.]
4. Linux: find the process that owns the port
ss (from iproute2) is the current tool. -l shows only listening sockets, -t TCP, -u UDP, -n keeps numbers, -p shows the process. In the test, with a throwaway listener started by python3 -m http.server 18765 --bind 127.0.0.1, this printed the program and PID in the last column:
ss -ltnp 'sport = :18765'
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 5 127.0.0.1:18765 0.0.0.0:* users:(("python3",pid=151164,fd=3))
The quoted 'sport = :PORT' filter is ss’s own syntax for source (local) port. Without it, ss -ltnp lists every listener; for UDP use ss -lunp.
lsof and fuser give the same answer. All three found the same PID in the test:
lsof -i :18765
lsof -nP -iTCP:18765 -sTCP:LISTEN
fuser -v 18765/tcp
lsof -i :18765 printed python3 151164 khenry 3u IPv4 ... TCP localhost:18765 (LISTEN). fuser takes a port/proto argument (the man page says the shortcut 80/tcp works), and -v shows user, PID and command like ps.
You only see your own processes unless you are root. On the test machine, ss -ltnp run as an ordinary user listed listeners that belonged to other users with the Process column empty, lsof -i :8143 printed nothing for one, and fuser 8143/tcp printed nothing. Empty output does not mean the port is free. Run the command with sudo to see every process, for example sudo ss -ltnp 'sport = :80' (sudo was not used in the test).
Then see what the PID is:
ps -p 151164 -o pid,user,comm,args
systemctl status 151164
systemctl status accepts a PID and names the unit that owns it. In the test, running it against a shell’s PID showed the owning systemd user manager unit. If the PID belongs to a service, the unit name is what you want to stop. If it prints nothing useful, the process is not a service, so it is a plain program or a leftover.
If you get “Address already in use” but ss shows no listener, check that you did not look at the wrong protocol (TCP versus UDP), the wrong address family (look at both the IPv4 and the [::] IPv6 rows), or a port that was only just released.
Root cause
Another process already holds the port you wanted. It is either something that should be there (a service you forgot about or a second copy of the same product), something that should not (a stale process), or, on Windows, the system itself (HTTP.sys or a reserved range). The error is the operating system doing its job. The fix is to remove the conflict at its source, not to work around the error.
The fix
Warning: stopping the owner can break things. Do not kill PID 4 (System), svchost.exe, lsass.exe, wininit.exe, csrss.exe, systemd or anything else you cannot name. Microsoft says of Stop-Process: stopping a process can stop processes and services that depend on it, and in an extreme case can stop Windows. Prefer stopping the service over killing a process, check what depends on it, and try -WhatIf first.
Choose the least destructive option that works, in this order:
- Move your program to another port. Often the easiest and the safest, especially if the owner is a legitimate service.
- Stop the service that owns the port, if it is not needed.
- Stop the stray process if it is a leftover.
Windows: stop a service
Use the service name from the Win32_Service query. Stop-Service sends a stop message to the Service Controller. If it has dependent services, -Force is needed to stop them too, which is exactly why you should list them first:
Get-Service -Name W3SVC | Format-List Name, Status, DependentServices
Stop-Service -Name W3SVC -WhatIf
Stop-Service -Name W3SVC -Confirm
W3SVC (IIS) is only an example. To keep a service from starting again at boot, change its startup type through the Services console or your normal configuration tooling. Do not disable a service you have not identified. [UNCONFIRMED: the exact command to set the startup type was not checked here.]
Windows: stop a process
For a plain program (not a service). Stop-Process -Id takes the PID; a process not owned by the current user needs PowerShell run as administrator. Without -Confirm it does not prompt, so add it, and Microsoft recommends the prompt because it shows the process name beside the ID:
Get-Process -Id 4321
Stop-Process -Id 4321 -WhatIf
Stop-Process -Id 4321 -Confirm
The cmdlet works asynchronously. Check that the process has gone and the port is free before you start your program. A process that is shared, protected or restarted by a service will come back or refuse to stop.
Linux: stop a service or a process
If systemctl status named a unit, stop that unit rather than killing its process, otherwise systemd may simply restart it:
sudo systemctl stop nginx.service
sudo systemctl disable --now nginx.service # only if it should not start at boot
nginx is only an example. For a plain process, ask it to exit and give it a moment, using the PID you checked:
kill 151164
kill sends SIGTERM, which lets the program shut down cleanly. Only use kill -9 if it ignores that, because SIGKILL gives it no chance to clean up. In the test, kill on the throwaway listener released the port immediately and ss showed nothing on it afterwards.
fuser -k kills without looking. fuser -k 80/tcp sends SIGKILL to every process using the port, and the man page says SIGKILL is the default signal. It is a quick way to hit the wrong process, or several. It was not run in the test. Use fuser -v first, then kill the specific PID.
Verification
Repeat the lookup. It should now show nothing listening on the port:
# Windows
Get-NetTCPConnection -LocalPort 80 -State Listen -ErrorAction SilentlyContinue
netstat -ano | findstr ":80 "
# Linux
ss -ltnp 'sport = :80'
lsof -i :80
On the test machine, after stopping the listener, ss -ltn 'sport = :18765' printed only the header line, after a second python3 -m http.server on that port had failed with OSError: [Errno 98] Address already in use while the first was running. Start your own program and confirm it listens: the same command should now show your program’s PID, not the old one.
Notes
- Bind address matters. A program on
127.0.0.1:8080and another on192.168.1.5:8080can coexist. One on0.0.0.0or[::]claims the port on every address, so a second program on a specific address can still fail. - Run as administrator or root to see everything. On Windows, Microsoft says
netstat -bfails without enough permissions, and Stop-Process needs an elevated prompt for processes you do not own. On Linux, process details for other users are hidden (shown in the test). A search that finds nothing is not proof that the port is free. - Check UDP separately. TCP and UDP ports are independent.
Get-NetTCPConnectiondoes not show UDP, andss -ltdoes not show UDP either. - The owner may be a container. A container with a published port can appear as the container runtime or its proxy process rather than the application you expect. [UNCONFIRMED: how each container runtime appears in netstat or ss was not tested here.] List running containers with your container tool before killing the process.
- Do not confuse “refused” with “in use”. “Connection refused” means nothing is listening on the port. “Address already in use” means something is. On Linux, binding a port below 1024 as an ordinary user fails with a permissions error instead. [UNCONFIRMED: not tested here; check your distribution’s settings for unprivileged low ports.]
- Sources: Microsoft Learn: netstat, Get-NetTCPConnection, Get-NetUDPEndpoint, tasklist, Stop-Process, Stop-Service, Win32_Service, netsh http, netsh interface, Windows Sockets error codes. Linux: the ss(8), lsof(8) and fuser(1) manual pages and systemctl(1) from systemd.
Port still blocked and no process in sight? Post the output of netstat -ano (or sudo ss -ltnp) and the excluded-port-range listing for your port, with anything private removed, in The Patch Panel and we will work through it. The best answer earns points on Top of the Stack.