Mac automation for developers,
minus the plist archaeology.
You already have the scripts. What you don't have is a harness that runs them reliably, tells you when they fail, and shows you what happened. If your Mac mini or MacBook is doing homelab duty on top of four shell scripts, two launchd plists, a Hazel rule, and a Shortcut, this is the tool that replaces the pile with one visual canvas and a run history you can actually read.
14-day free trial · no credit card · $49 one-time · macOS
Sound familiar?
A launchd plist is XML you hand-wrote once, got wrong twice, and haven't touched since. When it silently stops firing, there's no run history to check — just launchctl list and a shrug.
cron on a laptop is a polite fiction. Close the lid at 11:58 for a midnight job and it doesn't run late — it just doesn't run. No error, no retry, no notification. Watchflows can't fire through a full sleep either — but a missed run shows up as a gap in run history, with a failure outlet you can wire to a notification, instead of a silent no-op you only discover from the consequence.
Your automation is currently four shell scripts, two plists in ~/Library/LaunchAgents, a Hazel rule, and a Shortcut — and none of them know the others exist.
The job that mattered failed at 3am, and you found out from the consequence — a backup that didn't run, a disk that filled up — not from the job itself.
Four recipes for the box that's always on
Built from real Watchflows nodes. Every recipe below is a flow you can build in a few minutes.
Retire the plist
Outcome: a scheduled job you can see, replay, and get told about when it breaks.
The Schedule trigger handles both interval mode (every N minutes or hours — a folder sweep, an API poll) and calendar mode (a specific wall-clock time on the days you pick). Run Script executes Bash, Python, Node, Ruby, or AppleScript — anything with a shebang — and hands stdout, stderr, and the exit code downstream as ordinary payload values, no output-parsing required. Wire the failure outlet through a Compare node checking the exit code into a Notification node, and you have alerting launchd never gave you for free.
Webhook straight to a shell
Outcome: an HTTP POST runs a script on your Mac and reports back.
The Webhook trigger listens on loopback by default; LAN mode is opt-in and requires an auth token — it's never reachable from the open internet on its own. Interpolate the request body straight into a Run Script node with {{...}}, run whatever you'd normally curl-pipe-to-bash, then post the result to a Slack or Discord webhook URL (or anywhere else) with an API Request node. Need a public endpoint from outside your network — a GitHub push, a Stripe event? Swap in the Public Webhook trigger, delivered through the optional Watchflows Cloud relay instead of you opening a port.
Watch a build dir, run the tests
Outcome: artifacts land, tests run, a ticket opens when they fail.
Directory Changed watches a folder, optionally its subdirectories, and fires with the changed path in the payload — run a script on folder change without gluing together entr and a wrapper shell script yourself. Run your test command from a Run Script node, then let a Compare node branch on the captured exit code: wire the success branch to a notification of your choice, and the failure path files a Linear issue with the captured stderr interpolated straight into the description. A durable ticket instead of a terminal tab you already closed.
Keep an eye on the box
Outcome: know the always-on Mac is unwell before it takes something down.
The Resource Monitor emitter samples CPU, memory, disk, or battery on every tick and streams a payload continuously (one per workflow — add another flow if you want to watch a second metric at the same time). Threshold it with a Compare node, and drop the Time Series or JSON Viewer utility on the canvas to watch the trend build instead of eyeballing a single sampled number from top.
What tail -f and a plist will never show you
The reason all four recipes above matter more than the individual nodes is what surrounds them: a live execution timeline showing every payload as it moves between nodes, in real time, while the flow runs. Click any past run and replay it against the exact same payload — reproduce a bug without waiting for the bug to happen again. Inspect any node's input and output directly, the way you'd step through a debugger, instead of grepping a log file for the one line that matters.
Once a recipe works, it doesn't have to stay a standalone flow. The Run Watchflow action calls up to three of your other saved flows as part of a run — build "run the tests" once, then call it from a pre-deploy flow and a nightly flow both, instead of maintaining two copies of the same script.
And it's all just files. A flow is a local .watchflow document, not a row in someone else's database. No account, no cloud dependency for any of this to work, nothing to lose if a service you don't control has a bad day.
Why not just launchd, cron, or n8n?
launchd and plists. Native, free, and it's the actual right layer for a real daemon — more on that below. But the config is opaque XML, there's no run history, and a failure is silent unless you build logging into the job yourself.
cron. Search "cron alternative Mac" and every answer points you straight back to launchd, which brings you back to problem one. cron doesn't understand sleep at all — a job scheduled during the nap window doesn't run late, it just doesn't run.
Lingon and LaunchControl. If you've gone looking for a launchd alternative GUI or a launchd plist editor, you've likely found one of these — and they're genuinely good at what they do. But they're still editors for plists under the hood: a friendlier way to configure a scheduler, not a run history, a branch, or a place to chain the next step onto the last one.
n8n and Node-RED. Real node graphs, with the branching and visibility described above — genuinely comparable in shape. But that shape costs you Docker (or a standing Node service) to install, patch, and keep alive on a machine whose whole point was to stop being something you administer. Watchflows is self-hosted automation without Docker: one normal Mac app, nothing to containerize.
GitHub Actions. Excellent CI, and free for a lot of use cases — but it's a cloud runner. It cannot touch the Mac on your desk: no moving a local file, no AppleScript, no restarting a local service.
Where launchd still wins
launchd is still the right answer for a genuine system daemon, anything that must run before login, or anything that needs root. Watchflows runs in your user session — same as your Shortcuts, your menu-bar apps, and Lingon-managed LaunchAgents. If your job truly needs to be a LaunchDaemon, make it one. Consider having its last line hit a Watchflows webhook when it finishes, so you still get a run history and a place to branch on the result, instead of nothing.
Local-first, because it's your Mac
Flows are local files. Scripts run under your own user account with your own permissions, on the machine sitting in front of you or humming in a closet. Watchflows doesn't require an account, and nothing about running the recipes above needs its servers. If you wire in an AI node, it's bring-your-own key — Ollama and LM Studio run entirely on your network, and cloud provider keys (OpenAI, Anthropic, Google, OpenRouter) live in the macOS Keychain, never on our servers.
The one optional exception is Watchflows Cloud, a separate subscription that adds hosted AI and the public webhook relay used by the Public Webhook trigger in Recipe 2. Skip it entirely and the app, and every recipe on this page, is complete.
Questions from developers and homelab operators
Can Watchflows replace launchd entirely? ▼
For most user-session jobs — schedules, folder watches, webhooks, script-then-notify chains — yes. It's not a system daemon manager: anything that must run before login or needs root should stay a LaunchDaemon. See "Where launchd still wins" above.
Does it survive sleep better than cron, or drop jobs the same way? ▼
Watchflows runs in your user session, same as a launchd agent — it can't fire while the Mac itself is fully asleep either. What changes is visibility: a missed fire shows up as a gap in a run history you can actually look at, and a failure outlet you can wire to a notification, instead of a silent no-op you only notice from the consequence.
Can an external service like GitHub or Stripe trigger a script on my Mac? ▼
The base Webhook trigger is loopback by default, with an optional auth-required LAN mode — not reachable from the internet on its own. For senders outside your network, the Public Webhook trigger delivers through the optional Watchflows Cloud relay so you don't have to open a port.
What languages can the Script node run? ▼
Bash, Python, AppleScript, Node, or Ruby — anything you'd put behind a shebang. stdout, stderr, and the exit code become ordinary payload values the next node can read or branch on.
Do I need Docker or a background service running for this? ▼
No. Watchflows is a normal macOS app. There's nothing to containerize, no separate service to patch or restart, and no port to expose unless you explicitly turn on LAN mode for a webhook.
What does it cost? ▼
$49 one-time, including all v1 updates, after a 14-day free trial with no credit card required. Watchflows Cloud is a separate, optional subscription for hosted AI and the public webhook relay — the app is complete without it.
Watchflows for…
Stop debugging plists in the dark.
14-day free trial, no credit card. $49 one-time after that, all v1 updates included. Local-first, no account.
Questions? support@watchflows.app