- 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
root
user of the machine
- Basically the same thing as a daemon, but runs on behalf of the logged-in user, not the
root
user
- Important Distinction: You don't interact with
launchd
directly- Use
launchctl
to 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
.plist
file 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
launchctl
to 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
.plist
running 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
.plist
file, 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
.plist
file is valid for loading intolaunchd
with just theLabel
andProgram
orProgramArguments
attributes, but the background job won't run, since it doesn't know when to invoke it - It's an
xml
document - Some key attributes to include:
Label
(required): The name of your job, should be unique and follow "reverse domain" naming conventionProgram
(required ifProgramArguments
isn't present): The path to the executable on your systemProgramArguments
(required ifProgram
isn'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:
Month
Day
Weekday
Hour
Minute
- 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
PATH
when 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.zshrc
file 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>
Hi, is
StartCalendarInterval
a required attribute? I'm trying to run a .plist file every 2 minutes and noticed that it doesn't have that specific attribute, only the StartInterval....