←→ Underneath: Background Apps · All Mac guides

Why an app comes back after you restart your Mac

You quit it. You even force quit it in Activity Monitor. Two seconds later, or after the next restart, it is running again. Something is starting it on purpose. On a Mac, that something is almost always launchd.

Short answer: the app has a launchd job. RunAtLoad starts it at every login, and KeepAlive starts it again whenever it stops. Quitting the process does not change the job. To stop it for good, disable the job, not the process.

What launchd is

launchd is the first process macOS starts (process ID 1). It starts almost everything else. It reads small .plist files, called jobs, that say what to run and when. Each job has a label, a unique name like com.google.keystone.agent.

Apps install jobs in ~/Library/LaunchAgents, /Library/LaunchAgents or /Library/LaunchDaemons, or register them from inside their own app bundle.

The keys that bring an app back

Here is a typical launch agent:

<key>Label</key>
<string>com.example.helper</string>
<key>ProgramArguments</key>
<array>
  <string>/Applications/Example.app/Contents/MacOS/Helper</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
KeyEffectWhat you see
RunAtLoadStart the job as soon as it loads, which is at every login (agents) or startup (daemons)It is there after every restart
KeepAlive = trueRestart the process whenever it exitsForce quit does nothing; it is back in seconds
KeepAlive with conditionsRestart only in some cases, such as after a crash (SuccessfulExit = false) or while the network is upIt comes back sometimes, not always
StartIntervalRun every N secondsIt appears, runs briefly, disappears, repeats
StartCalendarIntervalRun at set times, like cronIt appears at the same time every day
WatchPathsRun when a file or folder changesIt appears after you do something unrelated

launchd waits about 10 seconds between restarts of a job that keeps exiting, so a KeepAlive job can also look like it “flickers”.

Other reasons an app comes back

Find which one it is

  1. Find the job label. Run launchctl list | grep -i example, using part of the app or developer name. The third column is the label.
  2. Read the job:
    launchctl print gui/$(id -u)/com.example.helper
    Look for path (the plist file), state, runs (how many times it started) and last exit code. A high run count usually means KeepAlive.
  3. Open the plist and check RunAtLoad, KeepAlive and the interval keys.
  4. If there is no job, check Open at Login and the app's own settings.

Stop vs. disable

What you doComes back?
Quit or force quit the processYes: right away if KeepAlive, at next login if RunAtLoad
launchctl bootout gui/$(id -u)/labelNot now. Yes at next login if RunAtLoad
launchctl disable gui/$(id -u)/label + bootoutNo, until you run launchctl enable
Move the plist out of LaunchAgents + bootoutNo, until you move it back
Turn off in Allow in the BackgroundNo, until you turn it on

Step-by-step commands are in how to disable launch agents safely.

FAQ

Why does a process restart right after I quit it in Activity Monitor?

Its launchd job has KeepAlive set. launchd watches the process and starts it again when it exits. Stop the job with launchctl bootout instead of quitting the process.

What does RunAtLoad mean?

RunAtLoad is a key in a launchd plist. When it is true, launchd starts the job as soon as it loads the plist, which for a launch agent means every time you log in.

What does KeepAlive mean?

KeepAlive tells launchd to keep the job running. If the process exits or is killed, launchd starts it again. It can also be a set of conditions, such as restart only after a crash.

What is the difference between stopping and disabling a launch agent?

Stopping (launchctl bootout) unloads the job until the next login or restart. Disabling (launchctl disable, or moving the plist out of LaunchAgents) stops it from loading again after a restart.