Skip to content
shellmap

Kill the process listening on a port

Find and terminate whatever process is bound to a TCP/UDP port — the answer to "address already in use" when a previous run did not release the socket.

How to kill the process listening on a port in each shell

Bashunix
lsof -ti :3000 | xargs kill

`-t` returns PIDs only (terse output), feeds directly into `kill`. Add `-9` if SIGTERM is ignored. Empty input → `xargs` runs `kill` with no args and exits non-zero — use `xargs -r` (GNU) to skip empty.

Zshunix
lsof -ti :3000 | xargs kill
Fishunix
kill (lsof -ti :3000)

Fish command substitution is `(…)`, not `$(…)`. If no process is bound, `lsof` returns empty and `kill` errors — wrap with `set pids (lsof -ti :3000); test -n "$pids" && kill $pids`.

PowerShellwindows
Get-NetTCPConnection -LocalPort 3000 -ErrorAction SilentlyContinue | Select-Object -Expand OwningProcess | ForEach-Object { Stop-Process -Id $_ -Force }

`Get-NetTCPConnection` is Windows-only (NetTCPIP module — present on Win8+/Server 2012+). On PowerShell 7 cross-platform, fall back to `lsof` on macOS/Linux.

cmd.exewindows
for /f "tokens=5" %a in ('netstat -ano ^| findstr :3000 ^| findstr LISTENING') do taskkill /F /PID %a

Double the `%` to `%%` inside a `.bat` file. `^|` escapes the pipe for `for /f` to consume the netstat output, not to receive it on stdin.

Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.

Gotchas & notes

  • **Choose the right port-lookup primitive per OS**: macOS still has `lsof` as the canonical answer (`lsof -i :PORT -sTCP:LISTEN -t`). Modern Linux replaced `netstat` with `ss` from iproute2 — `ss -ltnp sport = :3000` (`-l` listening, `-t` tcp, `-n` numeric, `-p` show PID/process — `-p` requires CAP_NET_ADMIN or root for OTHER users' sockets). On Linux `lsof -i` walks `/proc/*/fd/*` and is much slower than `ss` on busy hosts (`/proc` scan is O(processes×fds)). `fuser 3000/tcp` is a third option (psmisc package). Windows: `netstat -ano` (the `-o` shows OwningPID — added in XP SP2) or the modern `Get-NetTCPConnection -LocalPort 3000` (richer object output: `State`, `LocalAddress`, `OwningProcess`, plus joinable to `Get-Process` for command-line context).
  • **IPv4 + IPv6 dual-bind catches you out**: a Node/Python server bound to `::` (all IPv6 interfaces) on a dual-stack system accepts both v4 and v6 traffic but `lsof -i4 :3000` shows nothing — it only matches v4 sockets. Use bare `lsof -i :3000` (no `-i4`/`-i6` filter) or pass `-i6` explicitly. `ss -ltnp` shows BOTH families by default but distinguishes them by `tcp` vs `tcp6` in the state column. On Windows, `Get-NetTCPConnection -LocalPort 3000` returns BOTH stacks; check `LocalAddress` (`::` = v6 all, `0.0.0.0` = v4 all, `127.0.0.1` = v4 loopback only).
  • **TIME_WAIT and the "I killed it but the port is still busy" trap**: a TCP socket the killed process held in `ESTABLISHED` enters `TIME_WAIT` for ~60s (2×MSL) after close — during which time `bind()` to the same `(IP, PORT)` returns `EADDRINUSE` unless the new app sets `SO_REUSEADDR` (almost all production servers do, but `python -m http.server` and Node's default `http.createServer` do NOT). Verify with `ss -ant state time-wait sport = :3000`. Workarounds: wait it out; set `SO_REUSEADDR` in the new app; or `sysctl -w net.ipv4.tcp_tw_reuse=1` system-wide (Linux — dangerous for NAT environments, can confuse upstream load balancers). Don't reach for `tcp_tw_recycle` — REMOVED from Linux 4.12 because it broke NAT.
  • **UDP sockets**: there's no listening state for UDP — `ss -lunp sport = :53` (`-u` for UDP, `-l` for "listening" which for UDP means "bound and reading"). `lsof -i udp:53`. `Get-NetUDPEndpoint -LocalPort 53` on Windows. UDP servers (DNS resolvers, NTP clients, mDNS) often bind on startup and stay forever — kill semantics are identical (`Stop-Process` / `kill`), only the lookup tool differs from TCP.
  • **Cross-platform script** (works on macOS + Linux): `(command -v ss >/dev/null && ss -ltnp "sport = :$PORT" | grep -oP "pid=\K\d+") || lsof -ti :$PORT` — prefers `ss` where available, falls back to `lsof`. Wrap in a `kill-port` shell function once and reuse.

Related commands

Related tasks