Find the Process Using a Port on Mac (lsof)
You start a dev server and get address already in use, or an app refuses to bind its port. Something is already listening there, and lsof will name it in one line. Once you have the process ID, you can quit or kill it cleanly — the steps are identical on macOS 13 Ventura, 14 Sonoma, and 15 Sequoia.
Name the process with lsof
# Who is using port 3000?
sudo lsof -i :3000
Typical output:
COMMAND PID USER FD TYPE NODE NAME
node 41284 you 23u IPv6 TCP *:3000 (LISTEN)
The two columns you care about are COMMAND — the process name — and PID, the number you'll need to stop it. (LISTEN) confirms it's the one holding the port open. The sudo matters: without it, lsof only reports processes you own, so a system daemon squatting on the port would show up as nothing at all.
Don't be surprised by two lines for the same PID — one IPv4 and one IPv6 entry for a single server is normal, and it's still just one process to stop. It's also worth knowing that ports below 1024 are privileged: only root can bind them, so whatever you find on port 80 or 443 is almost certainly a system service or something you installed with elevated rights, not a stray user app.
Quit or kill it the right way
Prefer the gentlest exit that works. If the process is an app, quit it normally with Cmd–Q; if it's a dev server you started, press Control–C in its Terminal tab. When neither is possible, use the PID:
# Polite: ask the process to shut down cleanly (SIGTERM)
kill 41284
# Force: only if it ignores the polite version (SIGKILL)
kill -9 41284
kill on its own gives the process a chance to save state and exit cleanly. kill -9 cannot be caught or ignored — the process dies instantly, along with any unsaved work — so treat it as the last resort, not the default. If the process belongs to root or another user, prefix with sudo.
After killing, re-run the lsof command to confirm the port is actually free. If the same command instantly shows a new PID on the same port, you're not fighting a stray process — you're fighting a managed one that launchd or a supervisor restarts on exit. In that case killing it repeatedly is pointless; disable the service or login item that spawns it instead.
Know the usual squatters
- AirPlay Receiver — since macOS 12, the ControlCenter process listens on ports
5000and7000, which collides with Flask and other dev tools. Turn it off in System Settings → General → AirDrop & Handoff → AirPlay Receiver and the ports free up immediately. - Stale dev servers — a
nodeorpythonprocess left over from an editor window or terminal you closed without stopping the server. - Second instances — another copy of the same app or a Docker container already publishing the port you're trying to use.
Audit everything that's listening
# Every listening TCP port, numeric, no DNS lookups
sudo lsof -i -P -n | grep LISTEN
-P shows port numbers instead of service names and -n skips reverse-DNS lookups, so the list is exact and fast. Running this occasionally is a good habit beyond debugging — you'll see precisely which apps and services are accepting connections on your Mac.
While you're cleaning up ports, Mainspring cleans up everything else: it turns 90+ hidden macOS settings into labelled, reversible toggles — one click on, one click back off.
Try Mainspring free →Signed & notarized by Apple · 1-day free trial · $29 once
If the same port is busy after every reboot
A port that's occupied every time you log in points to a login item or background service starting automatically. Check System Settings → General → Login Items for the offender — our guide to managing startup items on Mac walks through hunting down the ones that hide in LaunchAgents.