Lesson 5 of 7 · 7 min

The PID is not always the owner

Killing a PID can stop the current process, but launchd, Homebrew, an editor, or a desktop app may own the lifecycle and start it again.

What you'll learn

  • The mental model
  • Find the PID
  • Inspect the process

A PID is a process ID.

It is useful, but it is temporary. It tells you which running process owns something right now. It does not always tell you who is responsible for starting it, restarting it, or keeping it alive.

That distinction matters.

You can kill a PID and still watch the port come back two seconds later.

The mental model

A PID is the person currently standing at the door.

The owner is whoever hired that person, gave them instructions, and may replace them if they disappear.

The owner might be:

  • a terminal session
  • an npm script
  • a Python command
  • Homebrew services
  • Docker
  • launchd
  • an IDE
  • a menu bar app
  • a database app
  • Ollama or another local model service

If you only kill the PID, you may not stop the owner.

Find the PID

First find the listener:

lsof -nP -iTCP:3000 -sTCP:LISTEN

Example:

COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
node    48217 you   21u  IPv4 ...        TCP 127.0.0.1:3000 (LISTEN)

Here, the PID is 48217.

Inspect the process

Now ask the system for more detail:

ps -p 48217 -o pid,ppid,user,stat,comm,args

The PPID is the parent process ID.

If the parent is your shell, the process probably came from a terminal command.

If the parent is an IDE, launcher, service manager, or background app, killing the child may not be the right control method.

Inspect the parent

Take the PPID and inspect it too:

ps -p <PPID> -o pid,ppid,user,stat,comm,args

Example pattern:

PID     PPID USER STAT COMM  ARGS
48217   48190 you  S    node  node ./node_modules/.bin/vite
48190   48002 you  S    zsh   -zsh

This suggests a terminal launched the dev server.

Another pattern:

PID     PPID USER STAT COMM  ARGS
11844   1    you  S    redis-server /opt/homebrew/etc/redis.conf

A parent of 1 often means a service manager is involved. On macOS, that frequently points toward launchd or a service wrapper.

Kill is not a management strategy

This command asks a process to stop:

kill <PID>

This command forces it:

kill -9 <PID>

Use kill -9 only as a last resort. It does not give the process time to clean up files, sockets, locks, or child processes.

More importantly, it does not tell the real owner to stop.

If Homebrew is managing the process, use Homebrew:

brew services list
brew services stop <service-name>

If Docker is managing it, use Docker:

docker ps
docker stop <container-name-or-id>

If your terminal started it, go to that terminal and press:

Control-C

When killing the PID is fine

Killing the PID is reasonable when:

  • you started it manually
  • it is stuck
  • you know it is not supervised
  • you only need to free the port temporarily

Example:

kill 48217

Then check whether the port is free:

lsof -nP -iTCP:3000 -sTCP:LISTEN

No output usually means no listener remains.

When killing the PID is suspicious

Be more careful when:

  • the process immediately comes back
  • the parent process is 1
  • the process name is a database or system service
  • the process came from Docker
  • the process came from an IDE
  • you do not recognize the command

The question is no longer “How do I kill this PID?”

The better question is:

What owns this lifecycle?