Two ways to schedule things
On Linux servers, cron is the historical scheduler. On macOS, Apple deprecated cron in favor of launchd — a service manager that doubles as a scheduler. Both are still around, but launchd is the recommended path on a Mac.
cron quick reference
crontab -e # edit your cron table
crontab -l # list it
*/5 * * * * /path/to/script.sh
0 3 * * 1 /path/to/weekly.shFive fields: minute hour day-of-month month day-of-week. cron runs as a stripped-down shell with a tiny PATH. Always set absolute paths and source what you need explicitly: 0 3 * * * . $HOME/.zshenv && /path/to/script.sh.
launchd LaunchAgent
Place a plist in ~/Library/LaunchAgents/. Load with launchctl bootstrap gui/$UID ~/Library/LaunchAgents/com.me.mytask.plist. Schedule via StartInterval (seconds) or StartCalendarInterval (cron-like dictionary). Survives reboot, supports auto-restart on crash.
<plist>
<dict>
<key>Label</key> <string>com.me.daily</string>
<key>ProgramArguments</key> <array><string>/path/to/daily.sh</string></array>
<key>StartCalendarInterval</key>
<dict><key>Hour</key><integer>3</integer><key>Minute</key><integer>0</integer></dict>
<key>StandardOutPath</key> <string>/tmp/daily.log</string>
</dict>
</plist>Pippa note: APScheduler in-process
Pippa's heartbeat schedules don't use cron or launchd. They run inside pippa serve via APScheduler. launchd's only role is keeping pippa serve alive. The schedules themselves are in-process for ergonomics. So crontab -l on the office Mac is empty — that is by design.
When to use which
- Linux server → cron (or systemd timers).
- macOS → launchd LaunchAgent (user) or LaunchDaemon (root).
- App-internal schedules → embed a scheduler (APScheduler, node-cron) and let the OS keep your process alive. Often the cleanest path.