launchctl “new” subcommand basics for macOS


For services running in the background (or foreground), macOS uses launchd (think cron jobs on Linux or scheduled tasks on Windows).

Legacy Subcommands

If you’ve been managing Macs for a while, you may be familiar with a particular syntax for loading launchd.

For launch agents (usually run as user), you may typically have launched them with

launchctl load /Library/LaunchAgents/

For launch daemons (usually run as root), you may typically have launched them with

sudo launchctl load /Library/LaunchDaemons/

If you read the manual for launchctl (man launchctl), you’ll see load and unload listed as legacy subcommands:

Legacy subcommands should still work. I don’t believe they’re fully deprecated (at least as of this writing).

“New” subcommands

But there are now subcommands that aren’t legacy:

For a super-comprehensive breakdown on what all these “new” subcommands are, you may want to check out Babo D’s Launchctl 2.0 Syntax post (from 2016!), but I thought I’d just do a quick breakdown here of the basics.

launchctl subcommand basics

Don’t come for me. As I mentioned above, there are far more comprehensive breakdowns of launchctl subcommands. This is just the basics. Not much nuance. Just how do you do it if you’ve been doing it the “legacy” way this whole time.

Listing with launchctl

With legacy subcommands, to get launch agents, you’d run

launchctl list

and that would give you a list of launch agents that are running.

With the “new” subcommands, you would do something like

launchctl print gui/501

Well, that’s assuming your uid is 501, which it may not be. If you want to find your uid, you can use id -u. If you want to find the uid of a specific other user account, you can use id -u username. You could also find the uids of a bunch of users by running dscl . -list /Users UniqueID | grep -v _

With legacy subcommands, to get launch daemons, you’d run

sudo launchctl list

or run launchctl list as root (e.g., via Munki or Jamf).

With the “new” subcommands, you would do

launchctl print system

The nice thing about doing that is you’re specifying system, so it doesn’t matter if you run it as root or as user—you’ll just get the system (root-level) launchd.

With either type of listing type, you won’t just get a list of process IDs and labels (you will still get those under services), but you’ll also get a list of what are called disabled services, which is a bit confusing, because that list will include both disabled and enabled services.

So, for example, for Nudge, you might see something like

0 0 com.github.macadmins.Nudge

under services but then something like

"com.github.macadmins.Nudge" => enabled

under disabled services.

You’ll probably find ways to parse the output, but just be aware there is this note in man launchctl:

IMPORTANT: This output is NOT API in any sense at all. Do NOT
rely on the structure or information emitted for ANY reason. It
may change from release to release without warning.

Launching launch agents

Instead of running

launchctl load /Library/LaunchAgents/com.github.macadmins.Nudge.plist

you’d run something like

launchctl bootstrap gui/501 /Library/LaunchAgents/com.github.macadmins.Nudge.plist

Launching launch daemons

Instead of running

sudo launchctl load /Library/LaunchDaemons/com.github.macadmins.Nudge.logger.plist

you’d run something like

sudo launchctl bootstrap system /Library/LaunchDaemons/com.github.macadmins.Nudge.logger.plist

Obviously, if you’re running this in a script from a root-run management tool (e.g., Munki or Jamf), you wouldn’t preface commands with sudo.

Unloading launch agents

Instead of running

launchctl unload /Library/LaunchAgents/com.github.macadmins.Nudge.plist

you’d run something like

launchctl bootout gui/501/com.github.macadmins.Nudge

Unloading launch daemons

Instead of running

sudo launchctl unload /Library/LaunchDaemons/com.github.macadmins.Nudge.logger.plist

you’d run something like

sudo launchctl bootout system/com.github.macadmins.Nudge.Logger

Reminder: don’t preface with sudo in root-run scripts.

Disabling a launch daemon or agent

Instead of bootout, you would use disable to disable a launch daemon or agent. Example:

sudo launchctl disable system/com.github.macadmins.Nudge.Logger

Just be careful with this. If you disable it, you can’t then just bootstrap it right afterwards:

sudo launchctl bootstrap system /Library/LaunchDaemons/com.github.macadmins.Nudge.logger.plist
Bootstrap failed: 5: Input/output error

First, you want to re-enable it:

sudo launchctl enable system/com.github.macadmins.Nudge.Logger

Then you can bootstrap it:

sudo launchctl bootstrap system /Library/LaunchDaemons/com.github.macadmins.Nudge.logger.plist

That’s all I’ve got for now. As I mentioned earlier, you can read a more comprehensive breakdown at Launchctl 2.0 Syntax.






One response to “launchctl “new” subcommand basics for macOS”

  1. alanysiu Avatar

    Thanks to Fridolin Koch on the MacAdmins Slack for catching a typo in the original post. I’ve since fixed it.

Leave a Reply

Your email address will not be published. Required fields are marked *