A Custom LaunchAgent for Nudge
Planted February 4, 2025

What is Nudge for macOS? What does it accomplish?
Nudge is an open-source tool designed to encourage (or, let’s be honest, nudge) macOS users to update their operating systems. It displays non-blocking (the users can continue working), progressively more persistent notifications reminding users to install available updates. The goal is to improve update compliance within organizations, ensuring devices are running secure and supported versions of macOS.
How does Nudge launch to tell users to update their Macs?
Nudge uses a LaunchAgent to trigger its checks and display notifications. This LaunchAgent is configured to run periodically. When it runs, Nudge checks for pending software updates. If Nudge is configured and updates are available, Nudge displays a notification to the user.
So, let’s create a custom LaunchAgent for our Nudge deployment
First, some important background info:
Read in-depth about launchd at https://www.launchd.info/
Or, skip to checking out the plist generator: https://launched.zerowidth.com/
In our case, the important information was
- What we’re going to launch/execute/run
/Applications/Utilities/Nudge.app/Contents/MacOS/Nudge
- When we’re going to launch/execute/run it
- Well, when we’re not going to run it, actually
By default, the LaunchAgent you can download from the Nudge GitHub releases launches Nudge every 30 minutes, if the user has not deferred longer than “later”.
Specifically, we don’t want Nudge to launch during class times, so we’re going to exclude those times by specifying them in cron
format.
Pressing “Create Plist” at the bottom of the generator will show you the formatted plist xml file. This is what we need to replace on or deploy to our fleet.
Let’s use a Package to deploy the new LaunchAgent
Using Composer, it’s really easy to create a new .pkg file to deploy via Jamf or your favorite MDM.
From your system, open Finder to /Library/LaunchAgents
, and place the plist we generated there. Next, drag the plist onto Composer to create a new package. I added some files in /Users/Shared
like a screenshot and an icon to include in Nudge.
Make sure the plist is owned by
root:wheel
and the mode is 644
in the bottom right corner.
Next, edit the postInstall
script in Composer to do a couple of things:
- See if a user is logged in (useful for #2 and #4)
- Unload the existing LaunchAgent if it exists (stops it)
- Step 2a is to place the new plist in place, but we’ve already done that with this package install
- Load the new LaunchAgent
- Run the Nudge postinstall script provided by the Nudge team
Here is a link to my GitHub repo with this script: custom_LaunchAgent.sh
Side note about launchctl
I’m using the newer versions (not load/unload) of launchctl commands. I really wish it had better error messages than “I/O Error” for multiple error message situations. Anyways, if a user isn’t signed in, we don’t need to bootstrap the LaunchAgent because as soon as a user signs in, all of /Library/LaunchAgents
loads anyways.
Moving on…
Deploying
Now, grab the Nudge package from GitHub - I’m using Nudge-2.0.12.81807.pkg but if you need something else to better fit your deployment, read up!
Upload both the Nudge.pkg
and the nudge-resources.pkg
we created to your MDM and scope them to your target devices. Don’t worry, this won’t run Nudge yet! It will only place the files on their computers.
Finally, upload your completed mobileconfig file to your MDM. I would suggest scoping that to your test devices first, to see if it runs correctly, then deploy it to a larger and larger scope to see if anyone yells as Nudge starts running.
So, Why do we need to deploy a custom LaunchAgent for our school?
Nudge was too annoying. You can configure a ton of things for the Nudge UI, but you can’t configure the time of launching. There was one instance of a “perfect storm” when MacOS 15.2 was released and I was using “latest-available” instead of specifying a version like “15.1.1” to mitigate a SOFA issue where a handful of M1 Macbooks were telling the end users that their devices were out of warranty and needed to be replaced by IT.
Turns out, when the SOFA feed was updated with 15.2 and the Required Install Date was in the past, a bunch of users had entirely blurred out screens by Nudge, even those who had correctly updated to 15.1.1! Also, I’m hosting my own SOFA feed now. Read about hosting your own to lighten the load of the default one.
So now, Nudge is only launching in off hours, we’re specifying a version (15.3) and a Required Install Date (about a month from now) so that hopefully everyone has time to install it before Nudge gets pushy.
Remember, never deploy on Fridays
Was this guide helpful?
Consider using my Amazon referral code to purchase something you were already going to purchase! Just add my tag to the Amazon product URL:
?tag=starbuckstech-20
For example, here’s an awesome USB-C retractable charging cable with my affiliate code:
https://www.amazon.com/dp/B0CZDJTRPZ?tag=starbuckstech-20
Thank you!!
Written in Markdown on Obsidian.md - February 2025