portpeek
Who is occupying my port 3000 / 5173 / 8080?
One command to find the process holding a port — and free it.
No more hunting in Task Manager, no more netstat -ano | findstr.
$ portpeek 3000
Port 3000 is in use:
PROTO PORT PID STATE PROCESS ADDRESS
----- ---- --- -------- ---------- ----------------
TCP 3000 452 LISTENING node.exe 0.0.0.0
TCP 3000 452 LISTENING node.exe [::]
UDP 3000 452 LISTENING node.exe 0.0.0.0
To free it: portpeek 3000 --kill
Why
On Windows, this loop is familiar:
- Local server fails to start
Error: listen EADDRINUSE- Who has the port?
- Dig through Task Manager
- Fight admin / non-admin prompts
portpeek does one job well: show who holds a port, and kick it out.
Install
git clone https://github.com/jsunnx/portpeek.git
cd portpeek
pip install -e .
Or run without installing:
python -m portpeek
Requires Python 3.9+.
| OS | How it reads ports |
|---|---|
| Windows (primary, tested) | netstat -ano + PowerShell process map |
| Linux | ss -tulnp + /proc (best-effort, less tested) |
| macOS | lsof (best-effort, less tested) |
If you only care about Windows, it is fully supported. POSIX paths are community-grade.
Usage
portpeek # list LISTEN / bound ports (TCP + UDP, IPv4 + IPv6)
portpeek 3000 # inspect one port
portpeek 3000 5173 8080 # inspect several ports
portpeek --pid 1234 # reverse: which ports does this PID hold?
portpeek --name node # filter by process name substring
portpeek --free # print a free TCP port (from 3000)
portpeek --free 8000 # free port starting search at 8000
portpeek --watch 2 # poll until the port state changes
portpeek --sort name # sort by port | pid | name
portpeek --all # include non-LISTEN states
portpeek --json # JSON object with schema_version=1
portpeek --json -o ports.json # write UTF-8 JSON file
portpeek 3000 5173 --kill # batch free
portpeek 3000 --kill --force
portpeek 3000 --kill --unsafe # allow killing protected OS processes
portpeek --no-color # plain text (CI / pipes)
JSON shape (schema_version is stable for scripts):
{
"schema_version": 1,
"count": 2,
"entries": [
{"proto": "TCP", "local_addr": "0.0.0.0", "port": 3000, "pid": 452, "state": "LISTENING", "process_name": "node", "exe_path": "..."}
]
}
System process protection
--kill never targets critical OS processes by default:
System (PID 4), Idle, lsass, csrss, smss, wininit, winlogon, services, Registry, Memory Compression, dwm, …
If only a protected process holds the port, you get a clear skip message.
Override only with --unsafe (combine with --force if needed) — you can blue-screen or lock yourself out. Don't.
JSON / redirect notes (Windows)
- Prefer
portpeek --json -o out.json— writes UTF-8 with no BOM. - PowerShell 5.1
>re-encodes text to UTF-16. For byte-faithful redirect use:
cmd /c "python -m portpeek --json > out.json"
# or
python -m portpeek --json -o out.json
Kill semantics
- Only listening holders are killed (not
TIME_WAITleftovers). - Windows uses
taskkill /T(process tree);--forceadds/F. - After kill, success means the port is no longer listening.
- Access denied → message suggests running as Administrator.
Common cases
| You hit this | Run this |
|---|---|
| Next / Vite won't start | portpeek 3000 |
| Who owns this PID? | portpeek --pid 4521 |
| Where is node? | portpeek --name node |
| Need a free port for a script | portpeek --free |
| Script needs JSON | portpeek 8080 --json |
| You must use that port | portpeek 8080 --kill |
Design notes
- Zero third-party deps
- Read-only by default:
--killis explicit - IPv4 + IPv6 kept separate (dual-stack not merged)
- UDP included: Windows UDP rows have no State column
- Portable JSON:
ensure_ascii=True - UTF-8 console on Windows for process names
Library API
from portpeek import list_ports, find_port, find_by_pid, find_by_name, next_free_port, kill_port
list_ports()
find_port(3000)
find_by_pid(4521)
find_by_name("node")
next_free_port(3000)
kill_port(3000, force=False)
Tests
python -m unittest discover -s tests -v
License
MIT
If this saves you one Task Manager scavenger hunt, a Star is enough. Issues welcome.
Metadata
Release files for portpeek 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| portpeek-0.4.0.tar.gz | 15.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| portpeek-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.6 kB
Release files / portpeek-0.4.0.tar.gz
| Download URL | portpeek-0.4.0.tar.gz |
|---|---|
| Size | 15.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
30bf48a4a8b9b867d9d408857017ad008c41c5d6125e37a218dae23e4d8704a9
|
|
BLAKE2b-256 checksum How to use checksums |
4a1d96277a9ee9a012327b266dd38d1c5f7bcb05132665c8d79cc7663e6a4735
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|
Release files / portpeek-0.4.0-py3-none-any.whl
| Download URL | portpeek-0.4.0-py3-none-any.whl |
|---|---|
| Size | 13.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
26a641ce82a123e51e17cd1f1a1ad7844ad407b896df4d923419e3d1e413937f
|
|
BLAKE2b-256 checksum How to use checksums |
d82c2b0c4f98e5fc9ac60259f5f09b44bfb802e5005c9396537507d1b81323f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|