LocalRun

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.

TierWho supervisesSurvives quitSurvives crashSurvives reboot
manualYouNoNoNo
localrunThe daemonNoYesYes, via the daemon's own login item
alwayslaunchdYesYesYes

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 means pnpm, bun, and an nvm-installed node are all invisible. The agent puts <project>/node_modules/.bin first, 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. KeepAlive is never a bare true. 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.
Stopping an always-on project demotes it

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.