How to find orphaned Node processes on your Mac
You closed the terminal, but node is still holding port 3000 and 600 MB of RAM. When a process’s parent goes away, macOS hands it to launchd (PID 1). That makes orphans easy to find: they’re dev tools whose parent PID is 1.
Updated
List the orphans
Show your own processes whose parent is launchd, filtered to common dev tools:
ps -xo pid,ppid,etime,rss,command \
| awk '$2 == 1' \
| grep -Ei 'node|bun|deno|vite|esbuild|tsc|next|python|ruby|uvicorn|gunicorn' \
| grep -v '/Applications/'
PID PPID ELAPSED RSS COMMAND
48211 1 2-03:14:09 412880 node /Users/you/code/acme-web/node_modules/.bin/next dev
51007 1 05:41:22 98304 node /Users/you/code/api/node_modules/.bin/tsc --watch
51120 1 05:41:20 61440 /Users/you/.../esbuild --service=0.24.0 --ping
-xincludes processes with no controlling terminal, which is exactly what an orphan is. Without-a,pslists only your user’s processes, so nothing from the system appears.ELAPSEDisdays-hours:minutes:seconds. Anext devrunning for two days is almost certainly forgotten.RSSis in kilobytes: 412880 is about 400 MB.
The quicker version with pgrep:
pgrep -u "$USER" -P 1 -lf 'node|vite|python'
-P 1 matches only children of PID 1, -f matches against the full command line, and -l prints it.
See how much memory they hold
ps -xo ppid,rss,command | awk '$1 == 1 && /node/ && !/Applications/ {s += $2} END {printf "%.0f MB\n", s/1024}'
Check before you kill
An orphan is a candidate, not a verdict. For each PID, look at where it runs and whether it’s serving anything:
lsof -a -p 48211 -d cwd -Fn | tail -1 # project folder
lsof -nP -a -p 48211 -iTCP -sTCP:LISTEN # ports it holds
Leave alone anything you started on purpose with nohup or pm2, anything your editor manages, and anything not in your own projects.
Stop them
One at a time:
kill 48211 51007 51120
kill sends SIGTERM, which lets each one exit cleanly. If one ignores it after a few seconds, use kill -9 <PID>.
Avoid pkill node or killall node. They match every Node process you own, including the ones your editor’s language servers, Electron helpers or a running agent depend on. If you want a bulk command, restrict it to orphans and check the list first by running the pgrep line above without the kill:
pgrep -u "$USER" -P 1 -f 'vite|next dev|tsc --watch' | xargs kill
Why they happen
Most orphans come from one of these:
- Closing the window instead of Ctrl+C. Usually the shell forwards the hang-up and the server dies. Not always: watchers, background jobs and processes started with
nohupkeep going. - A killed parent.
npm run devstartsnode, which may start its own workers. Kill thenpmprocess with-9and its children get re-parented to launchd instead of stopping. - Helper services. esbuild, Turbopack, the TypeScript server and some test runners spawn long-lived service processes that sometimes outlive the tool that started them.
- AI agents. Claude Code, Codex and Cursor run dev servers and watchers to check their own work. When the session ends, anything detached stays behind. See cleaning up after Claude Code.
Stop making new ones
- Press Ctrl+C before closing a tab that runs a server. Terminal and iTerm2 warn when you close a window with a running process; leave that warning on.
- Run long-lived servers in one place you’ll notice, such as a tmux session or your editor’s task runner, rather than scattered tabs.
- Use
trap 'kill 0' EXITat the top of shell scripts that start background jobs, so the jobs stop when the script does.
An orphan often shows up first as a port conflict. If you got here from EADDRINUSE, see address already in use on Mac.
Doing this with Stat Space
Stat Space’s orphan sweep runs this check continuously. It looks only at dev-tool processes (node, python, vite, tsc and similar) re-parented to launchd after their terminal or agent went away, and shows the total, for example “Free 2.1 GB · kill 7”. Each row has its age, memory and any ports. It never touches system processes and always asks before stopping anything.
From the terminal, statspace orphans prints the list and statspace orphans --kill stops them after a confirmation. It’s part of the free Agents tab.
Questions
Is every process with parent PID 1 an orphan?
No. Every app you open from the Dock or Finder, and every LaunchAgent, has launchd as its parent. That’s why you filter to dev-tool command lines like node, vite or python and skip anything under /Applications.
Why didn’t closing the terminal stop my dev server?
Closing a window sends a hang-up signal to the shell, which passes it to its jobs. Processes started with nohup, & plus disown, tools that ignore the hang-up, and grandchildren whose middle parent was killed with kill -9 all survive it.
Are processes in tmux or screen orphans?
No. Their parent is the tmux or screen server, which is still running. Reattach with tmux attach and stop them there.
Know what your Mac is doing, without adding to it.
Free, no account. Pro is $9.99 once for the first 100 buyers.