The lsof port cheatsheet for macOS developers
lsof is the standard tool for answering "what is using this port" on macOS. It is also one of the most cryptic command-line tools you will use this week. The man page is 300 lines of options, most of which you will never touch. This post i…
lsof is the standard tool for answering "what is using this port" on macOS. It is also one of the most cryptic command-line tools you will use this week. The man page is 300 lines of options, most of which you will never touch. This post is the cheatsheet — the 8 commands a working developer actually uses, with one-line explanations and the gotchas that bite first-time users.
If you find yourself looking up lsof syntax monthly, save this page.
The 8 commands you actually need
1. What is using port 3000?
lsof -i :3000
The most common invocation. Returns rows with COMMAND, PID, USER, FD, TYPE, DEVICE, SIZE/OFF, NODE, NAME. The PID column is what you want for kill.
2. Just the PID, nothing else
lsof -ti :3000
-t is "terse" — outputs only the PID. Useful for piping:
lsof -ti :3000 | xargs kill
The most common one-liner for "free up port 3000." See the Stack Overflow canonical answer for the full history.
3. All listening ports
lsof -i -P | grep LISTEN
-P shows numeric ports instead of service names (e.g. :3000 instead of :http). grep LISTEN filters out established connections. The result is the list of every process listening for incoming connections.
This is the "what is running on my machine" query.
4. All connections (not just listening)
lsof -i -P
Same as above but includes ESTABLISHED, TIME_WAIT, CLOSE_WAIT connections. Useful when debugging why a connection is hanging or why you have thousands of TIME_WAIT entries.
5. Listen on a specific port range
lsof -i :3000-3010
Lists everything on ports 3000 through 3010 inclusive. Useful when your dev environment uses sequential ports for multiple services.
6. Filter by IPv4 only
lsof -i4 -P | grep LISTEN
-i4 restricts to IPv4. The IPv6 noise (*:* placeholder rows) is one of the more confusing parts of default lsof output.
7. Connections to a specific host
lsof -i @localhost:3000
Reports only connections to localhost:3000. Useful when you want to confirm an outbound connection is happening (or not happening) to a specific destination.
8. By process name
lsof -i -P -c node
-c <name> filters by command name. Shows every port any node process is using. Useful when you have multiple Node services and want to see them all in one query.
The flags worth memorizing
| Flag | What it does |
|---|---|
-i | Filter to network sockets only |
-P | Show numeric ports (not service names) |
-n | Show numeric IPs (skip DNS lookup, much faster) |
-t | Terse output: PIDs only |
-c <name> | Filter by process command name |
-u <user> | Filter by user |
-i :<port> | Filter to a specific port |
-i <proto>@<host>:<port> | Full host/port filter |
Combine them: lsof -nP -i :3000 -t is the "give me just the PID listening on port 3000, do not resolve DNS, do not look up service names" canonical fast query.
Common gotchas
IPv6 stub rows
Default lsof output includes rows like:
node 12345 you 23u IPv6 0x... 0t0 TCP *:3000 (LISTEN)
node 12345 you 24u IPv4 0x... 0t0 TCP *:3000 (LISTEN)
Same process, listening on both IPv4 and IPv6. Two rows is normal, not a bug.
Permission denied for system services
lsof -i :22
# returns nothing if you are not root
System services (sshd, mDNSResponder, etc.) only show up under sudo lsof. For your own dev processes you do not need sudo.
Slow first run
Default lsof resolves DNS for every connection. On macOS this can pause for several seconds. Add -n to skip:
lsof -nP -i :3000
Always faster than lsof -i :3000.
Stale entries during process startup
If you just started a service, there can be a fraction of a second where lsof sees the socket but not the process name. Add a small sleep before checking, or query twice.
"No such file or directory" warnings
lsof: WARNING: can't stat() ... file system
Means lsof is trying to walk a mount that requires permissions. Usually safe to ignore for port queries; add -w to suppress.
Piping patterns
The five lsof pipelines worth knowing:
Kill everything on a port
lsof -ti :3000 | xargs kill
Kill everything by a specific process name
pgrep -f my-service | xargs kill
(Not strictly lsof, but adjacent.)
Count established connections to a port
lsof -i :8080 | grep -c ESTABLISHED
Watch port use over time
watch -n 1 'lsof -i :3000'
(watch is not built-in on macOS by default; install via brew install watch.)
Save the listener list to a file
lsof -i -P -n | grep LISTEN > listeners-$(date +%s).txt
Why lsof exists in the first place
A short historical note. lsof ("list open files") predates the modern Linux ss command and was designed to be portable across BSD-derived Unixes. On Linux, ss -tulpn is the modern replacement and is faster for the listening-ports query. macOS does not ship ss, so lsof remains the canonical answer on this platform.
You will see netstat recommended in old posts. macOS's netstat exists but does not include the PID column you need for the kill workflow, which is why lsof won the developer-tool argument.
When lsof is not the answer
Three cases where lsof is the wrong tool:
- Docker container ports.
lsofsees the host-side bind, not the container process. Usedocker psto see container ports. - Containerized macOS services (rare). Some virtualized environments hide their sockets.
- Kernel-level listeners. Some Apple system services bind ports without exposing them in
lsof. Addsudoif a port appears used butlsofshows nothing.
For 99% of "free port 3000" cases, plain lsof is the right tool.
A launcher-integrated alternative
If you want the kill-by-port flow without typing lsof syntax, install a launcher with a built-in command. CmdSpace has kill-by-port built in; Raycast has a community extension; Alfred has workflows. The interaction is "press hotkey, type port number." Under the hood it is the same lsof -ti :port | xargs kill.
Kill process by port on macOS covers the full ranking of methods. What is using port 3000 on my Mac covers the diagnostic side specifically.
The bottom line
Memorize three commands and you cover 95% of lsof use:
lsof -i :3000 # what is using port 3000
lsof -ti :3000 | xargs kill # free port 3000
lsof -i -P | grep LISTEN # everything listening
Everything else on this page is reference for the 5% of cases the three commands do not cover.
Sources
- Stack Overflow canonical "kill by port" answer — stackoverflow.com
- Stack Overflow on PIDs from piped processes — stackoverflow.com
- npm port-in-use thread — github.com/npm/npm
- yarn port-in-use thread — github.com/yarnpkg/yarn