| name | port-process |
| description | Find what's using a port, kill stuck processes, and manage system resources. Use when user mentions "port in use", "what's on port", "kill process", "lsof", "address already in use", "EADDRINUSE", "zombie process", "process management", "find PID", "free up port", "htop", "resource usage", or debugging port/process issues. |
Port and Process Management
Find What's Using a Port
lsof -i :3000
ss -tlnp | grep :3000
netstat -tlnp | grep :3000
lsof -i -P -n | grep LISTEN
ss -tlnp
lsof -i :PORT works on both macOS and Linux and is the most reliable first choice. On Linux, ss is faster and preferred over the deprecated netstat.
Kill a Process
kill 12345
kill -9 12345
lsof -ti :3000 | xargs kill
lsof -ti :3000 | xargs kill -9
pkill node
pkill -f "next dev"
killall node
The -t flag in lsof -ti outputs only PIDs, making it pipeable to kill.
macOS vs Linux Differences
| Task | macOS | Linux |
|---|
| Port lookup | lsof -i :PORT | lsof -i :PORT or ss -tlnp |
| Kill by port | lsof -ti :PORT | xargs kill | fuser -k PORT/tcp |
| Process tree | pstree PID (install via brew) | pstree -p PID or ps --forest |
| Network stats | netstat -an (no -p flag) | ss -tlnp or netstat -tlnp |
| File descriptors | lsof -p PID | ls /proc/PID/fd or lsof -p PID |
On macOS, ss and fuser are not available. Stick with lsof.
"Address Already in Use" Diagnostic Flow
When you hit EADDRINUSE or "address already in use":
lsof -i :3000
lsof -i :3000 -sTCP:LISTEN
ps -p <PID> -o pid,ppid,user,command
kill <PID>
lsof -i :3000
kill -9 <PID>
ss -tlnp | grep :3000
If the port is held by a process with PPID 1, it is orphaned. Kill it directly.
Find Processes by Name
ps aux | grep node
ps aux | grep -v grep | grep node
pgrep -la node
pgrep -f "next dev"
pidof node
ps -p 12345 -o pid,ppid,user,%cpu,%mem,etime,command
Process Tree
pstree -p 12345
ps -ejH
ps --forest -eo pid,ppid,cmd
pstree 12345
ps -o ppid= -p 12345
Useful for finding which shell or supervisor spawned a runaway process.
Background and Foreground
node server.js &
jobs
fg %1
bg %1
nohup node server.js > output.log 2>&1 &
node server.js &
disown %1
Resource Usage
top
htop
ps aux --sort=-%cpu | head -20
ps aux -r | head -20
ps aux --sort=-%mem | head -20
ps aux -m | head -20
free -h
vm_stat
Disk Usage
df -h
du -sh /path/to/dir
du -h /path | sort -rh | head -10
ncdu /path
find / -type f -size +100M -exec ls -lh {} \; 2>/dev/null
docker system df
docker system prune -a
File Descriptors
ulimit -n
ulimit -Hn
ulimit -n 65536
lsof -p 12345
lsof -p 12345 | wc -l
ls /proc/12345/fd | wc -l
for pid in /proc/[0-9]*; do
echo "$(ls $pid/fd 2>/dev/null | wc -l) $pid"
done | sort -rn | head -10
If a process hits the file descriptor limit, it logs errors like "too many open files." Raise ulimit -n or fix the file/socket leak.
Signals
| Signal | Number | Behavior | When to use |
|---|
| SIGTERM | 15 | Graceful shutdown, process can catch and clean up | Default. Always try first. |
| SIGINT | 2 | Same as Ctrl-C | Interactive stop |
| SIGHUP | 1 | Hangup. Some daemons reload config on SIGHUP. | Reload config (nginx, Apache) |
| SIGKILL | 9 | Immediate termination, cannot be caught | Process ignores SIGTERM |
| SIGSTOP | 19 | Pause process (like Ctrl-Z) | Temporarily freeze a process |
| SIGCONT | 18 | Resume stopped process | Unpause after SIGSTOP |
Always send SIGTERM first. Give it a few seconds. Only use SIGKILL (kill -9) when the process refuses to exit. SIGKILL skips all cleanup -- temp files, lock files, and sockets will be left behind.
kill PID
kill -HUP PID
kill -9 PID
Common Scenarios
Dev server won't start (port taken)
lsof -ti :3000 | xargs kill
Orphaned Node.js process eating CPU
pgrep -la node
kill <PID>
Multiple stale dev servers
pkill -f "next dev"
pkill -f "vite"
pkill -f "react-scripts"
Out of disk space
df -h
du -h /home --max-depth=2 | sort -rh | head -20
docker system prune -a
rm -rf node_modules/.cache
npm cache clean --force
Zombie processes
ps aux | awk '$8=="Z"'
ps -o ppid= -p <ZOMBIE_PID>
kill <PARENT_PID>
A zombie is a process that finished but whose parent hasn't read its exit status. Killing the parent lets init/systemd reap it.