
Open Terminal and run:
lsof -ti:3000 | xargs kill -9
Replace 3000 with the port you want to free. This finds the process listening on that port and force-kills it. The port is immediately available again.
The 5 methods at a glance
| Method | Speed | Best for |
|---|---|---|
1. lsof + xargs one-liner | 2 seconds | Daily dev use |
2. Find PID, then kill -9 | 5 seconds | When you want to see what is running |
3. killall by name | 3 seconds | Killing all Node, Python, Java processes |
| 4. Activity Monitor (GUI) | 15 seconds | Non-Terminal users |
5. Brow launcher: type kill 3000 | 1 second | Forgetting lsof syntax forever |
Method 1: The lsof one-liner (the dev favorite)
This is the command you should put in a shell alias. It frees a port in one step:
lsof -ti:3000 | xargs kill -9
Breaking it down:
lsof— macOS utility that lists open files (and on Mac, network sockets are files).-t— output only the process ID (no header, no extra columns). This makes the result pipeable.-i:3000— filter to the network connection bound to port 3000.| xargs— take the PID from stdin and pass it as an argument.kill -9— sendSIGKILLto that PID. The kernel enforces it; the process cannot trap or ignore it.
Pro tip: drop this in your ~/.zshrc as an alias so you never type it again:
killport() {
lsof -ti:$1 | xargs kill -9
}
Then just run killport 3000 any time a port gets stuck.
Method 2: Find the PID, then kill it
If you want to see what is actually running on the port before you kill it (for example, to confirm it is your dev server and not something important), use the two-step version:
lsof -i :3000
Output looks like this:
COMMAND PID USER FD TYPE NAME
node 51234 you 23u IPv4 TCP *:hbci (LISTEN)
Now kill it explicitly:
kill -9 51234
The flag matters. kill 51234 sends SIGTERM (signal 15), which is a polite "please shut down" message that some processes catch and ignore. kill -9 51234 sends SIGKILL (signal 9), which the kernel enforces immediately and the process cannot trap.
If kill returns Operation not permitted, the process belongs to another user (often root, if you started it with sudo). Prepend sudo:
sudo kill -9 51234
Method 3: killall by process name
Sometimes the issue is not one stuck Node server, it is fifteen of them, all spawned by a runaway nodemon or a debugger crash. Killing them by port one at a time is tedious. Kill them all by name:
killall node
Common variants developers use daily:
killall node— nukes every Node.js process. Useful whennpm run devhas spawned children that survived a Ctrl-C.killall pythonorkillall python3— same thing for Python servers.killall -9 java— force-kill Java processes (some refuseSIGTERM).killall ruby— useful for Rails servers.
Warning: killall node kills every Node process, including Electron apps you have running (VS Code, Slack, Discord, Figma). Use with care on a busy machine.
Method 4: Activity Monitor (the GUI option)
Not everyone wants to live in Terminal. If you prefer a graphical option:
- Open Activity Monitor (Cmd-Space, type "Activity").
- In the search box at the top right, type node (or
python,java, whatever your dev server is). - Click the Memory column to sort by usage — the runaway process is usually the highest.
- Select the process, click the X button in the toolbar, and choose Force Quit.
This is slower than the Terminal methods, and it does not let you target by port directly. But it works without remembering any commands and gives you a visual of what is actually running.
Method 5: Brow launcher (skip Terminal entirely)
If you find yourself typing lsof -ti:3000 | xargs kill -9 three times a day and wishing it were faster, here is a fix: Brow's launcher has a port killer built in. Press Option + Space, type kill 3000, hit return.
That is it. No Terminal, no remembering syntax, no shell alias to maintain. The launcher runs the same lsof + kill pipeline under the hood, but you never see it.
The launcher also has the rest of the dev shortcuts you forget five times a week: UUID generation, Base64 encoding, Unix timestamp conversion, JSON formatting, hashing (MD5/SHA-256), case conversion (camelCase, snake_case, kebab-case, CONSTANT_CASE), and a Mac-wide GIF picker. See the full launcher feature list.
Common Mac dev ports cheat sheet
| Port | Default for | Quick kill |
|---|---|---|
3000 | Node.js, Express, Next.js, Create React App | lsof -ti:3000 | xargs kill -9 |
3001 | Storybook, secondary Node servers | lsof -ti:3001 | xargs kill -9 |
4200 | Angular CLI | lsof -ti:4200 | xargs kill -9 |
5000 | Flask, AirPlay receiver (macOS!) | lsof -ti:5000 | xargs kill -9 |
5173 | Vite dev server | lsof -ti:5173 | xargs kill -9 |
8000 | Django, Python http.server | lsof -ti:8000 | xargs kill -9 |
8080 | Tomcat, Spring Boot, generic HTTP alt | lsof -ti:8080 | xargs kill -9 |
8888 | Jupyter Notebook | lsof -ti:8888 | xargs kill -9 |
9000 | PHP-FPM, SonarQube, Webpack dev server | lsof -ti:9000 | xargs kill -9 |
Heads up about port 5000 on macOS: Apple uses port 5000 for AirPlay Receiver. If you cannot bind to 5000 even after killing your Flask app, the issue might be macOS itself. Disable it in System Settings → General → AirDrop & Handoff → AirPlay Receiver.
Why "port already in use" keeps happening
Most of the time, the cause is one of four things:
- You closed the terminal without stopping the dev server. The process becomes orphaned and keeps holding the port.
- Your IDE crashed mid-debug. The debug adapter spawned a child process that survived the IDE crash.
- Hot reload spawned children that did not get cleaned up. Common with
nodemon,tsx watch, and Webpack dev server before v5. - A Docker container is bound to the port and you forgot. Run
docker psto check before you kill anything.
How to prevent it
You cannot eliminate this entirely — it is a fact of dev life — but you can reduce how often it happens:
- Use a process manager. Tools like PM2 or overmind handle child cleanup correctly.
- Trap signals in your dev script. If you wrap your server in a script, register handlers for
SIGINTandSIGTERMthat close the server before exiting. - Set
--kill-others-on-failwhen usingconcurrentlyto run multiple processes — if one crashes, the others get killed too. - Add the
killportalias above — you cannot prevent it, but you can make recovery cost two seconds instead of thirty.
Bonus: kill all dev processes at once
End-of-day cleanup. Save this in your shell rc and call it before you close the laptop:
cleanup_dev() {
for port in 3000 3001 4200 5000 5173 8000 8080 8888 9000; do
lsof -ti:$port 2>/dev/null | xargs -r kill -9 2>/dev/null
done
echo "All dev ports freed"
}
Run cleanup_dev and every common dev port is free.
Frequently Asked Questions
How do I kill port 3000 on Mac?
Run this command in Terminal: lsof -ti:3000 | xargs kill -9. The lsof -ti flag returns just the process ID listening on port 3000, and xargs kill -9 sends a SIGKILL signal to terminate it. The port is freed immediately.
Why does kill PID not work sometimes on Mac?
kill PID without a flag sends SIGTERM (signal 15), which asks the process to shut down cleanly. Some processes ignore or trap SIGTERM. Use kill -9 PID to send SIGKILL, which the kernel enforces and the process cannot ignore. SIGKILL is what you want when a port is stuck.
Do I need sudo to kill a process on Mac?
Only if the process was started by root or another user. If you started the dev server yourself, you can kill it without sudo. If you see Operation not permitted, retry the same command with sudo prepended: sudo kill -9 PID.
What does lsof -i :3000 do on Mac?
lsof stands for list open files. The -i :3000 flag filters to network connections on port 3000. It prints a table with the command name, PID, user, and connection state. Add -t to get just the PID, useful for piping to kill.
Why does "port already in use" keep happening on Mac?
It usually means a previous process did not exit cleanly. Common causes: closing the terminal without stopping the dev server (the process gets orphaned), nodemon respawns leaving zombie processes, Docker containers binding ports without cleanup, and crashed IDE debug sessions. Killing the port is a fix; preventing it requires trapping signals in your dev script or using a process manager.
Does fuser work on Mac for killing ports?
No. fuser is a Linux utility and does not ship with macOS. Use lsof on Mac instead. If you migrated from Linux and want fuser-like behavior, the equivalent is lsof -ti:PORT | xargs kill -9.