You start a development server and it stops immediately with “address already in use”. Something on your PC already owns port 8080, and Windows will not let two programs listen on the same port. This guide shows how to find out what that something is with the tools built into Windows 10 and 11, what the output means, and how to free the port without breaking anything else. The steps work for any port; 8080 is just the one that collides most often.
The symptom
Each runtime words the same error differently:
Node.js Error: listen EADDRINUSE: address already in use :::8080
Python OSError: [WinError 10048] Only one usage of each socket address
(protocol/network address/port) is normally permitted
Java java.net.BindException: Address already in use: bind
.NET SocketException (10048): Only one usage of each socket address ...
Underneath they are all Windows socket error 10048, WSAEADDRINUSE: the program asked to bind a listening socket to port 8080 and the port was taken.
Note
A different message, “An attempt was made to access a socket in a way forbidden by its access permissions” (error 10013, shown by Node.js as
EACCES), usually means the port is reserved by Windows rather than used by a program. Jump to When the port looks free but is unusable.
Why ports are exclusive
When a packet arrives for 127.0.0.1 port 8080, the TCP stack has to hand it to exactly one listening socket. Two listeners would make delivery ambiguous, so Windows refuses the second bind. The rule is per protocol: a UDP socket on 8080 does not block a TCP listener on 8080, which is why the commands below filter on protocol as well as port.
The usual owners of 8080 are mundane: an earlier instance of your own server that is still running (often a child process that survived the terminal being closed), a container, a proxy, Tomcat or Jenkins, or another project on the same machine. Occasionally it is a Windows service.
Find the owner with netstat
Open a terminal (Command Prompt, PowerShell or Windows Terminal; no elevation needed) and run:
netstat -ano | findstr ":8080"
The flags: -a shows all sockets, including listening ones; -n prints numeric addresses and ports instead of resolving names, which is faster and unambiguous; -o adds the owning process ID. findstr ":8080" keeps only the lines containing that text.
Proto Local Address Foreign Address State PID
TCP 0.0.0.0:8080 0.0.0.0:0 LISTENING 15324
TCP [::]:8080 [::]:0 LISTENING 15324
Proto is TCP or UDP. Local Address is the address and port the socket is bound to on this machine. Foreign Address is the remote end, which is always 0.0.0.0:0 or [::]:0 for a listener. State says whether the row is a listener (LISTENING) or a connection (ESTABLISHED, TIME_WAIT and so on); UDP rows have no state. PID is the process that owns the socket.
A row whose foreign address ends in :8080 is a connection to someone else’s port 8080, not a listener on yours. To see only listeners:
netstat -ano | findstr ":8080" | findstr "LISTENING"
Turn the PID into a process
tasklist /FI "PID eq 15324"
/FI applies a filter, and "PID eq 15324" matches on process ID.
Image Name PID Session Name Session# Mem Usage
========================= ======== ================ =========== ============
node.exe 15324 Console 1 61,412 K
Image Name is the executable. Session Name and Session# tell you which logon session it runs in: Console is the interactive desktop, Services is session 0, where services live. Mem Usage is the working set.
If you would rather skip the second step, netstat -abno prints the executable name under each row, but -b only works from an elevated terminal.
The PowerShell way
Get-NetTCPConnection returns the same table as objects you can filter and pipe:
Get-NetTCPConnection -LocalPort 8080 -State Listen
It returns LocalAddress, LocalPort, RemoteAddress, RemotePort, State and OwningProcess (the PID). When nothing matches, the cmdlet reports an error that no matching objects were found; in scripts add -ErrorAction SilentlyContinue.
Hand the PID to Get-Process:
Get-Process -Id 15324 | Select-Object Id, ProcessName, Path, StartTime
Path is the full path of the executable. It is blank when Windows will not let your account open the process (another user’s session, a service, a protected process); running the same command from an elevated PowerShell usually fills it in.
As one pipeline:
Get-NetTCPConnection -LocalPort 8080 -State Listen |
ForEach-Object { Get-Process -Id $_.OwningProcess } |
Select-Object Id, ProcessName, Path
For UDP use Get-NetUDPEndpoint -LocalPort 8080; UDP sockets have no state, so there is no -State parameter.
If you prefer a window, Resource Monitor (run resmon, open the Network tab, expand Listening Ports) shows image, PID, address, port and protocol in one list.
What the address tells you
| Local address | Meaning |
|---|---|
0.0.0.0:8080 |
Every IPv4 address on this machine; reachable from the LAN if the firewall allows it |
127.0.0.1:8080 |
IPv4 loopback only; nothing outside this PC can connect |
[::]:8080 |
Every IPv6 address; a dual-stack socket accepts IPv4 connections here too |
[::1]:8080 |
IPv6 loopback only |
192.168.1.20:8080 |
One specific interface |
Two rows with the same PID on 0.0.0.0 and [::] are one dual-stack listener, not two programs. A server bound only to 127.0.0.1 can look down when a client resolves localhost to ::1 first, and a server bound only to [::1] is not reachable at 127.0.0.1. Binding to both, or to the wildcard of each family, avoids the confusion.
When the owner is svchost.exe
svchost.exe hosts Windows services, so the name alone does not say which service has the port. Ask for the services inside that PID:
tasklist /svc /FI "PID eq 1116"
Image Name PID Services
========================= ======== ============================================
svchost.exe 1116 RpcEptMapper, RpcSs
PowerShell gives display names and state as well:
Get-CimInstance Win32_Service -Filter "ProcessId = 1116" |
Select-Object Name, DisplayName, State
On a developer port like 8080 this is rare; on 135, 1900, 5353 or 7680 it is the norm. The guide What is svchost.exe and why does it use so many ports? covers the service groups and how to tell a genuine copy from an impostor.
When the port looks free but is unusable
If netstat shows nothing on 8080 and binding still fails, the port may be inside a range that Windows has reserved. Hyper-V, WSL 2 and Docker Desktop reserve blocks of ports through the Windows NAT driver, and a reserved port cannot be bound even though no process is listening on it. List the reserved ranges:
netsh interface ipv4 show excludedportrange protocol=tcp
Protocol tcp Port Exclusion Ranges
Start Port End Port
---------- --------
1061 1160
5000 5099
8032 8131
50000 50059 *
* - Administered port exclusions.
If 8080 falls inside one of the ranges, that is the cause. Ranges marked * were added by an administrator (or by you) and stay put. The unmarked ones are allocated dynamically and can move after a reboot, which is why a port that worked yesterday may fail today. The troubleshooting guide Port in use but no process is listed on Windows walks through the fixes, from choosing another port to restarting the NAT service.
Free the port safely
In order of preference:
- Stop the other program the normal way. Press Ctrl+C in its terminal, close its window, run
docker stop <name>for a container, orStop-Service <name>for a service. The port is released the moment the socket closes. - Change your own port instead. Most servers read a
PORTenvironment variable or a--portoption. If the other listener is something you need (a colleague’s proxy, a company agent), this is the only fix that lasts. - Terminate the process, if you know what it is.
taskkill /PID 15324asks the program to close, which works for programs that have a window.taskkill /PID 15324 /ForStop-Process -Id 15324ends it immediately and loses unsaved work.
Warning
Do not force-kill a PID you have not identified. PID 4 is the Windows kernel,
svchost.execarries services that other programs depend on, and killing a database or an editor mid-write can corrupt its files. Stop services throughStop-Serviceor the Services app, nottaskkill.
Stop-Process -Id 15324
Watch for the parent-and-child trap: closing a terminal that ran npm start can leave the node.exe child alive. Always stop the PID that netstat reports, not the one that launched it.
PortWho: the same answer in one view
The commands above work, but they are three steps across several tools, and when the result is svchost.exe you need one more. PortWho is a small Utility Forge tool that answers the question once:
portwho 8080
For every TCP listener and bound UDP socket on that port it reports the protocol, local address and port, the PID, the process name, the executable path, the Windows services hosted by that process (so svchost.exe is explained in the same row) and whether the executable carries a valid Authenticode signature and who signed it. portwho with no argument lists everything, portwho --search node filters by process name, path, service or signer, and portwho --json produces structured output for scripts.
PortWho is read-only. It never stops a process, never touches a socket or a service, and never uses the network, not even for signature checks. It is currently in development: the engine, the command line and a desktop application with a port lookup box are implemented and tested internally, and there is no public download yet. The PortWho page tracks its status and the documentation describes the output in detail. Like netstat’s LISTENING rows, it covers listening TCP sockets, not established connections, and it does not yet list reserved port ranges; use the netsh command above for those.
FAQ
Frequently asked
Does netstat need administrator rights?
No. netstat -ano runs as a standard user and still shows the owning PID of every socket. Only the -b flag, which prints the executable name under each row, needs an elevated terminal. Use tasklist or Get-Process with the PID instead and you never need to elevate.
Why does port 8080 show up twice, once on 0.0.0.0 and once on [::]?
If both rows carry the same PID, it is one dual-stack listener that accepts IPv4 and IPv6 connections. Two different PIDs mean one program listens on IPv4 only and another on IPv6 only, which Windows allows because the two sockets do not overlap.
netstat lists a PID but tasklist cannot find it. What happened?
The process exited between the two commands, or it restarts constantly (a file watcher such as nodemon does this). Run both commands again straight after the failure, or use the single PowerShell pipeline in this guide. If the port keeps failing and nothing is ever listed, read the troubleshooting guide on ports that are in use with no process listed.
Can I just use a different port?
Yes, and it is usually the best fix. Pick a port above 1024 that is not inside the dynamic range (49152 to 65535 by default) and not inside an excluded range, and set it through your server's PORT variable or --port option so the choice is recorded in the project.
Is a server listening on 0.0.0.0 reachable from other computers?
Potentially. Windows Defender Firewall decides, and the first time a program listens on a non-loopback address Windows normally asks whether to allow it on private or public networks. If you only need local access, bind to 127.0.0.1 instead and the question never arises.