macOS: Run script on startup

MacOS uses launchd as the daemon for running services and other daemons.

To run a script or a few on startup, you can interface with launchd by writing a corresponding plist file. In my case, I want to run networksetup to connect to a specific VPN on startup, but to only run it once so that I can switch to other ones later.

Essentially, whenever my computer starts up, I want to run

$ networksetup -connectpppoeservice VPN_Name

First, let’s figure out where the actual executable we want to use is:

$ which networksetup
/usr/sbin/networksetup

Next, we’ll create a small plist to that describes what we want to run, and how. A plist is a specific kind of XML file.

<?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>com.karltarvas.startup</string>

    <key>OnDemand</key>
    <false/>

    <key>LaunchOnlyOnce</key>
    <true/>

    <key>UserName</key>
    <string>karltarvas</string>

    <key>ProgramArguments</key>
    <array>
        <string>/usr/sbin/networksetup</string>
        <string>-connectpppoeservice</string>
        <string>VPN_Name</string>
    </array>
</dict>
</plist>

Most of the keys have self-explanatory names, the crucial bit here is to set LaunchOnlyOnce to only run the service once.
ProgramArguments is an array of strings corresponding to the command you want to run with its arguments.

Once the above is set up, save the file as a .plist and move it into /Library/LaunchAgents/, for example /Library/LaunchAgents/com.karltarvas.startup.plist.

The file needs to be owned by root for launchd to play nicely with it:

sudo chown root:wheel /Library/LaunchAgents/com.karltarvas.startup.plist

The final step is to load up the file:

sudo launchctl load /Library/LaunchAgents/com.karltarvas.startup.plist

When your system starts up, the above command will be run along with any other services you’ve set up.