You kill whatever is on port 5432, run your app, and get the same error. The process is back with a new PID. Most guides stop at kill -9, which is the wrong fix for this. Here's the full routine, in the order I use it.
The one-liner
Find the process listening on the port and ask it to quit:
lsof -ti tcp:3000 -sTCP:LISTEN | xargs kill
-t prints only PIDs. -sTCP:LISTEN is the part most snippets leave out, and it matters: without it, lsof also returns every client connected to the port. If you have the app open in a browser, that includes the browser, and you quit it instead of the server.
A range of ports works too:
lsof -ti tcp:3000-3010 -sTCP:LISTEN | xargs kill
The careful version
When you're not sure what's on the port, take it in four steps:
- See what's listening:
lsof -nP -iTCP:3000 -sTCP:LISTEN - Check it's the right thing:
ps -p <PID> -o command= - Ask it to quit:
kill <PID> - Check again after a second. Only if it's still there:
kill -9 <PID>
Step 2 is the one people skip. node on port 3000 could be your app, or a different project you forgot was running.
kill, kill -INT or kill -9?
| Command | Signal | When to use it |
|---|---|---|
kill <PID> |
SIGTERM | First choice. The server closes files and connections, then exits. |
kill -INT <PID> |
SIGINT | What Ctrl-C sends. Some servers only clean up on this one. |
kill -9 <PID> |
SIGKILL | Last resort. It can't be caught, so nothing gets cleaned up. |
A database killed with -9 has to recover on its next start, and a dev server may leave a lock or socket file behind. Give SIGTERM a second before reaching for it.
Make it a command
If you do this every day, add a function to ~/.zshrc, open a new terminal, and run killport 3000:
killport() { lsof -ti tcp:"$1" -sTCP:LISTEN | xargs kill; }
There are npm tools too. npx kill-port 3000 sends SIGKILL straight away, with no grace period. npx fkill-cli :3000 stops the process on a port, and npx fkill-cli on its own opens an interactive picker.
If you do this several times a day: full disclosure, I built Portlivo, a Mac menu bar app that lists every dev server and port. Select one and press K, and it stops it safely, including Homebrew services and Docker containers. It's $8.99 once (early-bird) after a 7-day free trial.
Stop it by name instead
When you know what it is but not which port it took:
pgrep -lf "vite" # list matches first
pkill -f "vite" # then stop them
-f matches anywhere in the command line, so pkill -f node also stops your editor's helpers. Always run pgrep -lf with the same pattern first.
Check the port is free
nc -z localhost 3000 2>/dev/null && echo taken || echo free
If it still says taken a second after you stopped the process, something restarted it.
When it comes straight back
That's a supervisor doing its job. Homebrew services run under launchd with KeepAlive, so a killed Postgres is restarted within a second. Killing it harder doesn't help. Stop it where it's managed:
| What holds the port | Stop it with |
|---|---|
| Homebrew service (Postgres, MySQL, Redis) | brew services stop <name> |
| Docker or OrbStack container | docker stop <container> |
| pm2 | pm2 stop <name> |
| nodemon or a file watcher | Ctrl-C in the terminal that runs it |
| launchd agent | launchctl bootout gui/$(id -u)/<label> |
| Laravel Herd (nginx, PHP, dnsmasq) | Stop it from Herd |
Not sure which one it is? brew services list and docker ps cover most cases. For other launchd agents, look in ~/Library/LaunchAgents or run launchctl list | grep <name>.
"Operation not permitted"
The process belongs to root or another user. sudo kill <PID> works, but check what it is first. One to leave alone: ControlCe on port 5000 or 7000 is macOS's AirPlay Receiver. Turn it off in System Settings → General → AirDrop & Handoff instead.
That covers every way I free a port. If it saved you a trip to Stack Overflow, follow me for more Mac developer fixes like this one.
Top comments (0)