A Vite app from this morning, an artisan serve from yesterday, Postgres, Redis, and whatever your coding agent started in the background. By the afternoon most of us have no idea what's actually running. Here's how to see all of it from Terminal.
The one command
This lists every TCP port something is listening on, with the program and its PID:
lsof -nP -iTCP -sTCP:LISTEN
A typical afternoon looks like this:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 48213 you 23u IPv6 0x8c2f… 0t0 TCP *:3000 (LISTEN)
node 51877 you 24u IPv4 0x1d07… 0t0 TCP 127.0.0.1:5173 (LISTEN)
php 52210 you 8u IPv4 0x6b3a… 0t0 TCP 127.0.0.1:8000 (LISTEN)
postgres 611 you 7u IPv6 0x4e19… 0t0 TCP [::1]:5432 (LISTEN)
redis-ser 702 you 6u IPv4 0x93c4… 0t0 TCP 127.0.0.1:6379 (LISTEN)
ControlCe 423 you 9u IPv4 0x2f88… 0t0 TCP *:7000 (LISTEN)
Three things worth knowing when you read it:
-
*:3000accepts connections from other devices on your network.127.0.0.1or[::1]means only this Mac. - Names are cut to nine characters:
redis-seris redis-server, andControlCeis ControlCenter, which is macOS's AirPlay Receiver (it's why port 5000 is often taken). Add+c 0for full names. - One server can appear twice, once for IPv4 and once for IPv6, with the same PID.
Which project is it?
lsof says node or php, not "the shop frontend". To get the full command and the folder a process runs in:
ps -p <PID> -o command=
lsof -a -p <PID> -d cwd -Fn | sed -n 's/^n//p'
Or for every listening process at once, folder first:
for pid in $(lsof -ti -iTCP -sTCP:LISTEN | sort -u); do
printf '%s\t%s\n' "$(lsof -a -p "$pid" -d cwd -Fn | sed -n 's/^n//p')" "$(ps -p "$pid" -o command=)"
done
A folder of / usually means a system or app process, like an editor helper, rather than one of your projects.
Which ones can your phone reach?
Anything not bound to 127.0.0.1 or [::1] is reachable from your network:
lsof -nP -iTCP -sTCP:LISTEN | grep -vE '127\.0\.0\.1|\[::1\]'
That's what you want when testing on a phone, and worth checking on café Wi-Fi. Most dev servers only listen on localhost unless you pass --host (Vite) or -H 0.0.0.0 (Next.js).
Docker and Homebrew hide behind other names
Ports published by Docker show up in lsof as com.docker.backend (or OrbStack), not as your app. Homebrew services show as the database itself. List them directly:
docker ps --format '{{.Names}}\t{{.Ports}}'
brew services list
Why not Activity Monitor?
Activity Monitor shows processes with CPU and memory, but not their ports. You can double-click a process and open Open Files and Ports, one process at a time. Fine for one server, slow for ten.
Or keep it in the menu bar
I ran these commands often enough that I built Portlivo, a Mac menu bar app that keeps this list live: every server named by its project, with the command, uptime, whether it's answering, CPU and memory, and Docker and Homebrew services alongside. One click opens, restarts, tails or stops any of them. It's a native app with a 7-day free trial.
Takeaways
-
lsof -nP -iTCP -sTCP:LISTENshows every listening port. -
ps -p <PID> -o command=and the cwd lookup tell you which project it is. -
*in the address means your network can reach it;127.0.0.1means only you.
Top comments (0)