Schedule Timed Jobs on macOS with `launchd`

Cameron Nokes
InstructorCameron Nokes
Share this video with your friends

Social Share Links

Send Tweet
Published 5 years ago
Updated 3 years ago

launchd is a robust scheduled job automation tool on macOS that allows you to schedule a task to be run at recurring times. It's macOS's take on Linux's cron. In this lesson, we’ll schedule a simple bash script that empties the Trash to run every Monday morning at 10am.

First, we’ll create a plist file (a plist file is just XML) with our job configuration. In here, we’ll specify our bash script as the program to run. Then we move plist file to the ~/Library/LaunchAgents folder. Then we’ll use launchctl to load our configuration file, which will make the job active. We'll manually activate our job to ensure it's working with launchctl kickstart.

Instructor: [00:00] First, let's write our script to be run on a schedule. I'll create that. Note that I'm just in my users home folder here. You can put your script wherever you want. I'll go ahead and give execute permissions. Let's open that script.

[00:16] The first thing we'll want to do in here is do the Bash shebang at the top. If we don't have that, then Launchd won't know how to execute our scripts. I'll use AppleScript to empty the trash. This is the equivalent of opening the trash folder and clicking that Empty button.

[00:33] Let's configure our job now. I'll create a new file and call it local.empty-trash.plist. There's some reasoning behind that name, which I'll explain shortly. A plist file is an XML-formatted file. It's an XML format that Apple came up with. There's quite a bit of boilerplate involved in it. I'm going to go ahead and paste that.

[00:50] Our main configuration is going to go in this dict or dictionary element here. I'll do a key here. We'll do our label first. The way that these plist files work is that there's a key elements followed by the value below it. The value specifies the type.

[01:10] For our label here, I'm going to do the same as our file name. Technically, the label and the file name can be anything. It just has to be unique across your system. By convention, it's in this reverse domain name syntax, which is a common convention in a lot of Mac OS things.

[01:27] Next, let's add the program arguments key. That's value will be an array. In that array, we're just going to have a string, which is the file path to the script that we're going to execute.

[01:40] Next, we'll do the scheduling. For that, we're going to do start calendarInterval. The value of that is going to be a dicts. We'll do weekday. I want my script to run every Monday, so Monday is a one, Sunday is a zero, etc. I want it to run at 10:00 AM. Note that these are integer types.

[02:04] Let's jump back to our terminal. In your home folder, there's the library, and there's a launch agents folder. If we list it out, we can see there's some other applications, job configurations in here. We're going to move our file into this folder.

[02:22] All agents in this folder are loaded at start-up automatically. To manually load it and activate our job, we'll call the launch control commands. We'll say load, and we'll pass our plist file here. Now our job is loaded and active. To verify that, I'll call the list commands, and this lists out all active jobs. There'll be a lot of jobs in that list, so I can grep for our local string there. Cool.

[02:50] When you see here's our job, and it's loaded and active. I still have a few days before it's Monday, so let's kick it off manually. First I'll bring up my trash. You can see there's just one image file in there. Let's kick off our job manually.

[03:03] To do that, we'll use the kickstart subcommands. Then we have to pass our job's identifier, which is a weird string. We do GUI. We'll use the UID, global environment variable. This is our user's ID. Then we'll pass the label of our job. We run that, and then let's check our trash. It's empty, so it works. Awesome.

[03:26] If we decide that we don't like our job anymore and we want to unload it, we use launchctl unload, and we pass our plist again. Then if we list out them all and we'll grep for it, we'll see it's not there. Our job's unloaded and it won't ever be run. We can always reload it again and see that it's back.

[03:46] Launchd has lots of possibilities and configuration options. We just scratched the surface of what it's capable of. Check the Launchd documentation to see what they all are.

~ 47 minutes ago

Member comments are a way for members to communicate, interact, and ask questions about a lesson.

The instructor or someone from the community might respond to your question Here are a few basic guidelines to commenting on

Be on-Topic

Comments are for discussing a lesson. If you're having a general issue with the website functionality, please contact us at

Avoid meta-discussion

  • This was great!
  • This was horrible!
  • I didn't like this because it didn't match my skill level.
  • +1 It will likely be deleted as spam.

Code Problems?

Should be accompanied by code! Codesandbox or Stackblitz provide a way to share code and discuss it in context

Details and Context

Vague question? Vague answer. Any details and context you can provide will lure more interesting answers!

Markdown supported.
Become a member to join the discussionEnroll Today