A bookkeeping client of ours had a script that pulled new invoices every morning at 6am. It ran on cron. One week the laptop it lived on didn't wake from sleep in time, cron never fired, and nobody found out until three weeks of invoices had piled up unprocessed. Nothing crashed. Nothing errored. The job just silently never ran, which is exactly the failure mode cron is built to produce if you don't plan around it.
That story isn't really about cron being bad. It's about picking the wrong tool for the job without knowing there were other options. There are three distinct ways to make a piece of automation run on a schedule or continuously in the background, and each one has a different failure mode, a different granularity, and a different amount of babysitting it needs. This post walks through all three in plain English, with a comparison table and a decision guide at the end.
The Three Ways to Run Something Automatically
Before getting into the details, here's the one-sentence version of each:
- Cron is a clock. It wakes up, checks the time, and if a scheduled minute has arrived, it fires off a command and forgets about it.
- A daemon is a worker that never clocks out. It's a process that stays alive in the background, looping on its own, reacting to things as they happen instead of waiting for a schedule.
- launchd is Apple's answer to both problems at once. On macOS it schedules jobs like cron does, and it supervises long-running processes like a daemon manager does, and it can restart things that crash.
None of these is universally "better." They solve different problems. Understanding which one fits your situation is the difference between automation that quietly runs for years and automation that quietly stops running for weeks before anyone notices.
Cron: The Time-Based Scheduler
Cron has been running on Unix-like systems since the 1970s. It's a background process that wakes up once a minute, reads a list of scheduled jobs (the crontab), and for each job whose scheduled time matches right now, it launches the associated command. Then it goes back to sleep until the next minute.
A typical cron line looks like this:
That reads as "at minute 0 of hour 6, every day, every month, every day of the week, run this script." It's compact, it's been battle-tested for five decades, and it's available on essentially every Linux server and every Mac out of the box.
Where cron is strong
- Dead simple for a fixed schedule. "Run this at 2am every night" is one line, no code required.
- Ubiquitous. Every Linux box has it. It's the default scheduling mechanism in most tutorials, hosting providers, and server images.
- Stateless and low overhead. Cron itself uses almost no resources, and each job starts fresh with no memory leaks to worry about from a long-running process.
Where cron falls apart
- No built-in retry. If a job fails, cron doesn't know or care. It ran the command; whatever the command's exit code was, cron moves on. There is no automatic "try again in five minutes."
- Silent failures by default. Unless you explicitly redirect output somewhere and someone actually reads it, a failing cron job fails into the void. This is the single most common reason a business discovers its automation broke weeks after the fact.
- Not built for always-on work. Cron can't hold a persistent connection, watch a folder in real time, or maintain state across runs. Every invocation starts from zero.
- Minute-level granularity. The floor is one minute. If you need something to happen within seconds of an event, cron is the wrong shape entirely.
- No dependency awareness. If your Mac is asleep, off, or the cron daemon itself isn't running, that scheduled minute just never happens. Nobody is notified.
The core cron problem: it optimizes for "fire and forget," which is also its biggest liability. It will never tell you it stopped working. Something else in your stack has to watch it for that.
Daemons: The Always-On Worker
A daemon (the term comes from old Unix jargon, unrelated to anything supernatural) is a process that starts once and keeps running indefinitely, usually structured as a loop: check for work, do the work if there is any, wait a bit, repeat. It's the pattern behind most "listening" software, mail servers, message queue workers, chat bots, and monitoring agents.
The simplest possible daemon is a `while True` loop with a sleep in it:
That process starts once, holds its state in memory the entire time it runs, and reacts within seconds of new work arriving instead of waiting for a clock to strike. Real daemons are usually more sophisticated, listening for events instead of polling on a timer, but the underlying idea is the same: it's alive continuously, not launched-and-exited on a schedule.
Where a daemon is strong
- Sub-second to few-second reaction time. If a customer submits a form and you want a reply within moments, a daemon watching that inbox beats a cron job checking it once a minute.
- Holds state between events. A daemon can keep an in-memory queue, a cache, an open database connection, or a running tally, none of which survives between cron's fresh-start invocations.
- Naturally suited to "watch and react" work. File watchers, webhook listeners, socket servers, and message consumers are all daemon-shaped problems.
Where a daemon gets harder
- It has to manage its own crash recovery. If the process dies, nothing brings it back unless something else is watching it. A daemon with no supervisor is a ticking time bomb, one unhandled exception away from silently going offline until someone happens to check.
- Resource use is continuous, not momentary. A daemon holds memory and, depending on its loop, CPU the entire time it's alive, even during quiet periods. Cron's jobs only cost resources while actually running.
- Restart-on-boot is not automatic. Reboot the machine and the daemon is gone unless something is configured to relaunch it at startup.
- More moving parts to get wrong. Logging, graceful shutdown, memory leaks over long uptimes, and handling the machine going to sleep are all things a hand-rolled loop has to account for that cron never has to think about.
launchd: macOS's Native Answer to Both
launchd is the process that macOS itself uses to start almost everything on the machine, from system services to the Dock. Apple designed it to replace both cron-style time scheduling and the traditional Unix daemon-management tools in one system, and it has been the default init system on macOS since 2005 (10.4 Tiger). Cron still technically works on modern macOS, but Apple has treated it as legacy for a long time, and it comes with real caveats on newer macOS versions around permissions and background execution.
You configure launchd with an XML property list (a "plist" file) rather than a one-line crontab entry. That's more verbose, but it buys real capability. A launchd job can be told to run:
- On a calendar schedule (`StartCalendarInterval`), which is functionally cron's job, "run this at 6am every day."
- On a repeating interval (`StartInterval`), "run this every 300 seconds," something cron cannot express directly without workarounds.
- Kept alive continuously (`KeepAlive`), which turns it into a supervised daemon, if the process crashes or exits, launchd relaunches it automatically.
- Triggered by an event, such as a file appearing in a watched path, a socket receiving a connection, or the machine waking from sleep.
That last point is the real headline feature: launchd is the one tool of the three that can do both cron's job and a daemon supervisor's job, in the same system, using the same configuration format.
Where launchd is strong
- Automatic restart on crash. `KeepAlive` means a daemon-style job that dies gets relaunched without any custom supervisor code.
- Wake-aware scheduling. launchd knows about sleep and wake in a way cron historically has not handled well, a `StartCalendarInterval` job that was missed because the Mac was asleep runs as soon as the Mac wakes, rather than silently never firing.
- One system for both scheduled and always-on work. You don't need cron for the 2am job and a separate hand-rolled daemon manager for the always-on listener. It's the same mechanism either way.
- Native logging and system integration. Output plugs into the standard macOS logging tools, and launchd jobs show up in the same place system services do, which makes them easier to audit.
Where launchd is harder
- macOS only. None of this transfers to a Linux server, where the equivalent tool is systemd, a different system with a similar philosophy but a different configuration format entirely.
- More setup than a one-line crontab entry. A plist file is verbose, and small mistakes (a malformed key, a wrong path) fail in ways that are less obvious to debug than a crontab typo.
- Two separate domains to know about. User-level (`~/Library/LaunchAgents`) versus system-level (`/Library/LaunchDaemons`) jobs behave differently around permissions, whether they run when nobody's logged in, and whether they can show UI, and picking the wrong one is a common source of confusion.
Comparing All Three Side by Side
| Dimension | Cron | Daemon / Loop | launchd |
|---|---|---|---|
| What it is | Time-based scheduler that fires a command and exits | A process that stays alive and loops on its own | macOS's native service manager: schedules AND supervises |
| Best for | Fixed-time jobs on Linux servers ("run nightly at 2am") | Always-on, reactive work ("watch this inbox continuously") | Any macOS automation, scheduled or always-on |
| Scheduling granularity | 1 minute minimum | As fine as you code the loop, sub-second possible | Calendar-based (minute-level) or fixed-interval, seconds possible |
| Keeps itself alive / restarts | No | Only if you build that logic in | Yes (KeepAlive) |
| Platform | Linux, macOS (legacy) | Any OS, it's just a running process | macOS only |
| Failure handling | Silent by default, no retry, no alert | Depends entirely on what you build | Auto-restart on crash; wake-aware for missed runs |
Which One Should You Actually Use
Match the tool to the shape of the problem, not the other way around.
- A daily or weekly report, a nightly cleanup job, a Monday-morning digest. This is a clock problem. On a Linux server, cron is fine. On a Mac, use launchd's calendar trigger instead, same idea, better failure behavior.
- Something needs to react within seconds of an event, an inbound lead, a new file, a webhook. This is a daemon problem. Build a process that listens or polls tightly, and put a supervisor in front of it, don't just leave it running unwatched in a terminal tab.
- Any of the above, but it lives on a Mac. Use launchd for both cases. It's the one tool that covers scheduled jobs and supervised daemons under a single, restart-aware system.
- You genuinely don't know which shape your problem is. Ask whether a person would tolerate the task happening once a day versus needing to happen the moment the triggering event occurs. That answer tells you whether you need a scheduler or a standing worker.
Why Most Business Owners Shouldn't Be Managing This Themselves
Everything above is infrastructure, not the actual business logic. A restaurant owner who wants their vendor invoices checked automatically every morning does not need to know what a plist file is, or what `KeepAlive` does, or why their overnight job silently stopped running the week the laptop's clock drifted. That's exactly the kind of decision that should be made once, correctly, by whoever builds the automation, and then never thought about again.
This is one of the quieter reasons a lot of DIY automation (a Zapier chain, a script someone's cousin wrote, a scheduled task nobody documented) eventually breaks and stays broken. Nobody built in restart logic. Nobody wired up an alert for a failed run. The job just stops, and because cron and simple scripts fail silently by default, the business doesn't find out until something downstream goes visibly wrong.
When we build a custom agent for a client, the scheduling and restart logic is part of the build, not an afterthought bolted on later. If the task is genuinely a clock problem, it gets scheduled properly with retry and alerting. If it needs to be always-on, it gets supervised so a crash doesn't mean silence for three weeks. Either way, the owner never has to know or care whether it's running on cron, a daemon, or launchd, they just get told immediately if something needs their attention.
The real question to ask yourself: if the automation you're running today silently stopped working, how long would it take you to find out? If the honest answer is "days" or "weeks," the scheduling layer is the problem, not the automation itself.
If you want to see how this fits into a broader build, our guide on how AI agent implementation actually works covers what a properly built agent looks like end to end, scheduling, monitoring, and failure handling included. And if you're weighing whether a scheduled job or an always-on agent fits your process better, our AI agent use cases for small businesses breakdown walks through both patterns in real scenarios.
Let us worry about the scheduling
We build the automation and the infrastructure underneath it, restarts, retries, and failure alerts included, so you never have to think about cron, daemons, or launchd again. See what a custom build costs on our pricing page, or talk it through with us directly.
Book a 30-min callNo pitch. Just the math.
Frequently Asked Questions
What is the difference between cron and a daemon?
Cron runs a job at fixed times and then exits. It has no memory of what happened before and no idea what will happen after. A daemon is a process that stays running continuously, holding state in memory and reacting to events as they happen rather than waiting for a clock to strike. Cron is a scheduler. A daemon is a standing worker.
Is launchd better than cron on a Mac?
For most new work on macOS, yes. Apple has considered cron legacy on macOS for years, and launchd can do everything cron does (StartCalendarInterval) plus what cron cannot, restart a crashed process automatically (KeepAlive), run something the instant the Mac wakes up or a file changes, and log failures through the standard system logging tools. Existing cron jobs still run fine, but new automation on a Mac should default to launchd.
When should I use a daemon instead of cron?
Use a daemon when the task needs to react within seconds, not minutes, or needs to hold state between events, such as watching a folder, listening on a socket, or maintaining a queue of in-progress work. Cron's floor is one minute and it starts each run from zero. If the job is "do this thing every night at 2am," cron or launchd's calendar trigger is simpler and cheaper. If it's "watch this inbox and respond within seconds," a daemon is the only option.
What happens if a cron job fails silently?
Nothing, by default. Cron does not retry, does not alert anyone, and does not tell you the difference between a job that ran and failed versus a job that never ran at all. Output only goes somewhere if you redirect it yourself, and even then someone has to actually read the log. This is the most common reason businesses discover an "automated" process quietly stopped working weeks after it broke.
Do I need to know any of this to run AI automation in my business?
No. This is exactly the kind of infrastructure decision that should be invisible to a business owner. A properly built automation handles its own scheduling, its own restart logic, and its own failure alerts as part of the build, so you never have to think about cron, daemons, or launchd at all. You just need to know the job runs, and to hear about it immediately if it doesn't.