Keep running
Every project has one dial that decides who is responsible for keeping it alive. It is deliberately separate from what the project runs: a dev command under launchd still hot-reloads, and a production build under LocalRun still stops when you quit the app.
| Tier | Who supervises | Survives quit | Survives crash | Survives reboot |
|---|---|---|---|---|
manual | You | No | No | No |
localrun | The daemon | No | Yes | Yes, via the daemon's own login item |
always | launchd | Yes | Yes | Yes |
manual
Nothing starts it but you, and it stops when LocalRun quits. This is the right setting for anything you only want up while you are actually working on it.
localrun
The daemon starts the project when it starts, and restarts it if it crashes, with a backoff so a project that fails instantly does not spin. It still dies when LocalRun quits, because the daemon dies with it; pair it with launch-at-login if you want it back after a reboot.
always
LocalRun writes one LaunchAgent per project into ~/Library/LaunchAgents. macOS owns the process
from then on, so it survives quitting LocalRun, a crash, and a reboot.
The generated agent exists to defeat the things that only break on other people's machines:
- PATH. launchd hands a process
/usr/bin:/bin:/usr/sbin:/sbin, which meanspnpm,bun, and an nvm-installednodeare all invisible. The agent puts<project>/node_modules/.binfirst, then the usual install locations. If the first word of your command still cannot be resolved, LocalRun warns you when you set the tier rather than letting it fail silently at boot. - Restart policy.
KeepAliveis never a baretrue. That means "restart forever", and it makes Stop unwinnable; the process comes straight back and the app looks broken. The agent restarts on failure only, so a crash recovers but an explicit stop wins. - Logs. Output is pointed back at
~/.localrun/logs/, so the Logs tab keeps working once launchd owns the process instead of going dark.
If you explicitly stop a project on the always tier, LocalRun moves it back to manual.
Otherwise the next reboot would resurrect something you deliberately shut down.
Two copies are refused
Starting a project that is already running is rejected rather than silently allowed. A project with a cron schedule inside it would fire every job twice, which costs real money and is the kind of bug nothing else in the system would have caught.
This check runs before anything is routed to a supervisor, so pressing Start on an already-running always-on project tells you it is already up instead of quietly doing nothing.
Changing tiers
Set the tier in the project editor. Moving to always registers the agent immediately; moving away
from it unregisters and waits for the port to actually be free before reporting success; a fixed delay used to race launchd reaping the process, and the next start would fail to bind about one
time in three.
Next: Always-on walks through the setup end to end, including launch at login.