- Used on macOS for managing agents and daemons and can be used to run scripts at specified intervals
- macOS's competitor to
cron, along with other things
- macOS's competitor to
- Runs Daemons and Agents
A daemon is a program running in the background without requiring user input.
- Used to perform daily maintenance tasks or do something when an event occurs at the OS level, like when a device is connected.
- Runs on behalf of the
rootuser of the machine
- Basically the same thing as a daemon, but runs on behalf of the logged-in user, not the
rootuser
- Important Distinction: You don't interact with
launchddirectly- Use
launchctlto load or unload daemons and agents instead
- Use
- Steps to set up an agent or daemon:
- Create a program that you want to run in the background
- Create a
.plistfile describing the job to run (See below for how to author one) - Store it in the relevant spot based on whether or not you're creating a daemon or an agent, and why type of agent or daemon that you want to include (see screenshot below)
- Use
launchctlto load the job and set it to run:launchctl load PATH/TO-PLIST- Note that you only have to run this once when you first author / install the service. Upon reboot / login all agents & daemons will be loaded and run according to the
.plistrunning commands
- Note that you only have to run this once when you first author / install the service. Upon reboot / login all agents & daemons will be loaded and run according to the
- If you want to run a job regardless of its run conditions listed in the
.plistfile, you can run the following:launchctl start LABEL.OF.JOB
The majority of the learning that I needed in order to run my background job came from the first link in the Further Reading section below
- A
.plistfile is valid for loading intolaunchdwith just theLabelandProgramorProgramArgumentsattributes, but the background job won't run, since it doesn't know when to invoke it - It's an
xmldocument - Some key attributes to include:
Label(required): The name of your job, should be unique and follow "reverse domain" naming conventionProgram(required ifProgramArgumentsisn't present): The path to the executable on your systemProgramArguments(required ifProgramisn't present): Array of string arguments including the path to your executable and any other arguments- All strings are concatenated together with spaces between them to make up the full command
ServiceDescription: Human-readable description of your serviceWorking Directory: Can set the working directory when your program runsRunAtLoad: Should we run the job at boot time (for daemons) / login time (agents)?StartCalendarInterval: Dictionary used to specifycron-like running intervals, like "run this every day at 3:00 AM"- Available keys:
MonthDayWeekdayHourMinute
- Important Note: omitted keys are interpreted as
*
- Available keys:
StartInterval: Used to run the background job every n secondsStandardErrorPath: Useful for indicating a specific log file location for when errors arise in your serviceEnvironmentVariables: Allows you to set different environment variables that can be accessed as part of your program- Common entries here include setting your
PATHwhen running different scripts.- Given that your service is running outside of the context of loading a terminal / shell session, your shims and other enhancements usually found in a
.bashrc,.bash_profile, or.zshrcfile won't be loaded; therefore you have to load those up here or your service might not run as expected
- Given that your service is running outside of the context of loading a terminal / shell session, your shims and other enhancements usually found in a
- Common entries here include setting your
This is an example .plist file that I use to run a background job to sync my Obsidian-based Second Brain to the git-based version of the repo, which then runs some formatting and other processes.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>dev.johnturner.ObsidianFoamReconciler</string>
<key>ServiceDescription</key>
<string>Regular sync between Obsidian Second Brain vault and my git-based Second Brain</string>
<key>Program</key>
<string>/Users/johnturner/second-brain/sync-from-obsidian.sh</string>
<key>WorkingDirectory</key>
<string>/Users/johnturner/second-brain</string>
<key>StandardErrorPath</key>
<string>/Users/johnturner/Desktop/reconciler-error.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>
/usr/local/opt/gettext/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
</string>
</dict>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>11</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>RunAtLoad</key>
<false />
</dict>
</plist>

Does the plist file need to follow any specific naming conventions? For example, I noticed many of the plist files I already had seem to all begin with the word
com.. Right now I am replicating this convention, just in case. Is it necessary?