---
title: How to find which program is using a port, and stop it
description: "A service will not start because \"address already in use\" or \"port 80 is already in use\". This article shows how to find the program that owns the port on Windows and Linux, how to tell a normal service from a stray process, and how to stop it safely."
url: "https://keh-tech.net/kb/find-what-is-using-a-port/"
updated: "2026-10-15"
category: "How-to"
tags:
  - Linux
  - Networking
  - Ports
  - Troubleshooting
  - Windows
---

# How to find which program is using a port, and stop it

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:

1. **Move your program to another port.** Often the easiest and the safest, especially if the owner is a legitimate service.
2. **Stop the service** that owns the port, if it is not needed.
3. **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:8080` and another on `192.168.1.5:8080` can coexist. One on `0.0.0.0` or `[::]` 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 -b` fails 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-NetTCPConnection` does not show UDP, and `ss -lt` does 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](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/netstat), [Get-NetTCPConnection](https://learn.microsoft.com/en-us/powershell/module/nettcpip/get-nettcpconnection), [Get-NetUDPEndpoint](https://learn.microsoft.com/en-us/powershell/module/nettcpip/get-netudpendpoint), [tasklist](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/tasklist), [Stop-Process](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/stop-process), [Stop-Service](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/stop-service), [Win32_Service](https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-service), [netsh http](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/netsh-http), [netsh interface](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/netsh-interface), [Windows Sockets error codes](https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2). 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](/community/help/) and we will work through it. The best answer earns points on [Top of the Stack](/leaderboard/).
