Run bash (.sh) via launchd using a plist in /Library/LaunchAgents

I have a .plist file located in /Library/LaunchAgents which properly references a script called "test.sh" located in ~/Documents/Scripts. I have also tried moving this script to ~/Library/Scripts and /Library/Scripts. The script is chmod +x and runs perfectly well from manual terminal session. I have also chown to root:wheel... nothing helps.


When the script is called by launchd via the plist, I get "Operation not allowed" error. This is written to stderr and in launchd.log


Similar is so easy on Linux. I am finding it impossible on macOS.


In systems settings, terminal has full disk access. Also, the script is auto added to Login Items and Extensions and this is set to enable.


Why is this sooo hard? All I want to do is run a script every hour from a background process. It all works but Mac refuses to run the actual script.


Thoughts?


MacBook Pro (M4 Pro, 2024)

Posted on Oct 24, 2025 9:12 AM

Reply
Question marked as Top-ranking reply

Posted on Oct 24, 2025 9:41 AM

Neither macOS nor its LaunchAgent implementation have anything to do with Linux, and some learning requirement is needed on your behalf on macOS.


That .plist should reside in ~/Library/LaunchAgents. It needs no special permissions on it as 644 (e.g. -rw-r--r--) is fine with your username and group ownership. Then it distills to the syntax that you have used in the .plist.


Below is a LaunchAgent .plist that I wrote some time ago that launches a shell script every 15 minutes to check on battery percentage above a certain value that I pass to the script. That shell script also resides in ~/Library/LaunchAgents for continuity.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Disabled</key>
	<false/>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <false/>
	<key>Label</key>
	<string>com.local.battery_percent.plist</string>
    <!-- 3600 hourly, 1800 every 30 min, 900 every 15 min  -->
    <key>StartInterval</key>
    <integer>900</integer>
	<key>Program</key>
    <string>/Users/viking/Library/LaunchAgents/battery_percent.sh</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/viking/Library/LaunchAgents/battery_percent.sh</string>
        <!-- minimum battery percentage to start warnings
             this value will be passed as argument to program -->
        <string>90</string>
    </array>
</dict>
</plist>

With this setup, you can log out of your account and sign back in and the .plist will launch and then again every 15 minutes thereafter.

27 replies

Oct 24, 2025 7:45 PM in response to etresoft

Well, this is so frustrating. I spoke too soon.


The test script works fine as all it is doing is echoing to a log as a test. The real script works from a scheduling/launchd/plist perspective.. but however it is running, the script's rsync lines cannot access my user Documents and Pictures etc. directories (in some cases at all and in others without sufficient access to perform properly).


The script works perfectly fine if I manually run it. I mean come on, all I want to do is automate this to rsync every hour. Should not be this difficult.


Very frustrating. This is my computer, my files, my network, etc. If Apple is going to prohibit basic scripting, at least produce errors and documentation on what exactly is the prohibition. A perfectly well working script cannot be scheduled by the administrator of the system? So secure it is not useable?


I was hoping to use the Apple OS ecosystem/standards (in a sense) but nope. I guess I have to explore cron on macOS or something else.


And all this is due to I have significant investment in perfectly good, but older, network appliances that are unfortunately dependent on AFP for TimeMachine backups. So Apple kills AFP, but provides no workaround. In fact, my TimeMachines are literarily useless now. $ --> trash.


Vendors of older appliances certainly are not going to help either. So fine, rsync via ssh to the older NAS'.... only Apple is making that very difficult. Again, scripts work. Automation routes seem unsupported.

Oct 25, 2025 9:01 AM in response to MrHoffman

So I’m wondering whether the “upstream” question here is akin to “how to replace a Time Capsule and Time Machine?”, but here with a focus on issues arising with specific potential candidates for replacement of Time Machine backups to Time Capsule, and not a more general question about options and alternatives for use with macOS 26 “Tahoe” and the retirement of Apple Filing Protocol (AFP, and not to be confused with the Apple APFS file system), and of Time Capsule.


The replacement for Time Machine to Time Capsule is Time Machine backup to a Time Machine server. Available NAS vendors with support for Time Machine server include Synology, UGREEN, Ubiquiti, and others, and ZimaOS and TrueNAS among other options for bring-your-own x86-64 hardware NAS server builds.


Time Machine server uses SMB and mDNS / Bonjour, and this been supported for some seven (or more?) years, as has been using another macOS system as a Time Machine server target.


Pretty much anything with AFP support (past the old Apple Time Capsule gear, the newest of which is somewhere between roughly seven and twelve years old now) will have SMB support, as well. SMB support is much more common than AFP support ever was, as well.


The Time Machine server SMB replacement path became available at least seven years ago, too. That’s when I started down the Time Capsule replacement path: https://discussions.apple.com/thread/8373217?answerId=33351456022&sortBy=oldest_first#33351456022


As for performing the rsync via cron task, maybe this helps: https://rasterweb.net/raster/2025/03/17/scheduling-rsync-in-macos/


The more Apple-centric means of scheduling and scripting stuff involves using AppleScript to trigger the shell script. Though cron works, so long as the rsync job has access through protections and TCC / full disk access. And AppleScript having its own issues.


Oct 25, 2025 12:53 PM in response to etresoft

I guess rsync is a rabbit hole. Cool. Thank you for pointing out there are better ways to do backups than to use rsync. Perhaps a more specific pointer though? As in what did you have in mind?


I do like rsync -av -e ssh ... as it doesn't alter source and only operates on changed files and I don't particularly need time phased versioning of the individual backed up files; nice to have maybe, but not really necessary.


Happy to find a better way. I can fantasize a "Net change APFS snapshot to remote server" lol.

Oct 25, 2025 1:29 PM in response to MrHoffman

I have some old QNAP NAS'. They work great. They support SMB4 (command line enablement). However, they do not support Time Machine over SMB. Only AFP. Sad. And I don't really expect them to reopen dev on QTS 4 from EOL last year and other than replacing the DOM, I have not found away around this on the QNAPs.


But the NAS' work great as network destinations and new ones are $$$. Thus the desire to rsync ssh to them as one aspect of a back up plan. My TimeCapsules also still work well even though old. But now AFP is to actually finally die, they are not going to have any use case.


As for Apple Script, I actually have a version of this where an Automator step uses AppleScript to tell terminal to run the .sh. This is working same as manual running it. So now I need to find a manner for scheduling the AppleScript or Automator flow. The internet says as a Calendar event but this seems clunky as I want it running hourly. The internet also says cron and launchd too. But "internet says". LOL. I'll probably test calling the automator flow with cron and launchd and see if "operation not permitted" shows back up. And then maybe also a look at a stand alone Apple Script called by those as well.


Another poster on here said rsync isn't the way. So maybe there is something else to use/automate/script. I might also see if a python version of the scripts handles spaces and quotes and security more consistently when called via the differing automation avenues.

Oct 25, 2025 2:33 PM in response to Ashler

Ashler wrote:

I have some old QNAP NAS'. They work great. They support SMB4 (command line enablement). However, they do not support Time Machine over SMB. Only AFP. Sad. And I don't really expect them to reopen dev on QTS 4 from EOL last year and other than replacing the DOM, I have not found away around this on the QNAPs.


QNAP NAS with QTS 4.3.0 (or later) includes Time Machine server support, per:

https://www.qnap.com/en/how-to/tutorial/article/how-to-back-up-your-mac-to-qnap-nas-using-time-machine


If a spare x86-64 box and some attached storage is available, load ZimaOS or TrueNAS on that, and use that.

Oct 25, 2025 5:04 PM in response to MrHoffman

Unfortunately, not all QNAPs on QTS 4 are the same. My 859-Pro is an Atom. Consequently none of the SMB TimeMachine server options in those screens are available. The non-x86 QNAPs seem to require TM to be AFP.


I did figure out how to get SMB3 as highest level of SMB. This is via command line. But I cannot find any way of getting the TimeMachine server to be visible to a Mac without the QNAP having AFP enabled.


I also note when a TimeMachine backup starts, it is referencing an AFP share, not the SMB - again, as the SMB has no way of being told it is a TM backup destination. <insert frown face here>.


Like I found a command line for enabling SMB3 as the GUI on a non-x86 will not show the option, I was hoping to find a command for enabling SMB TimeMachine server. I have yet to find anything. QNAP support said buy a new QNAP. Hmmmm....

Oct 25, 2025 5:17 PM in response to etresoft

I use a lot of Python so might go there...


The space in the list is handled differently depending on how the .sh gets launched. Would need two versions. Not an option.


Backups to a cloud service would violate some of my client contracts. Or really, make it hard to show how I am protecting their data when transmitted and stored with a third party. Consequently I am zero cloud backups. I am not saying this is not possible.. it is just a legal department nightmare.


And true, I really do need to think of back ups differently. I have only SSDs (NAS, Externals, Internals) so actual loss from failure is unlikely. But... I travel full time. So the idea of back ups is about "house burns down" or laptop gets destroyed/stolen.... and cloud backups give me a legal headache. So.. ssh to other site (I own) plus external drives in safes (local and separate site).


I would love to just keep using TimeMachine.. but replacing NAS' that work... for the $$$ is just something I was hoping to avoid. And the manual approach has and does work well.... so... maybe play with the python approach. see if it is easy and then if not, stick with manual. It will work.

Oct 26, 2025 9:20 PM in response to Ashler

Ashler wrote:

And true, I really do need to think of back ups differently. I have only SSDs (NAS, Externals, Internals) so actual loss from failure is unlikely.

FYI, most SSD failures I have seen occur quickly with very little (if any) warning. The SSD crashing or disappearing is the most common symptom. The SSD may come back online a few times, but not always. And there is no way to detect that type of SSD failure because it involves the SSD's controller which has no hardware health monitoring.


I have seen an entire SSD model line from a respected SSD manufacturer have these sudden & permanent SSD failures (it is their low end budget line, but that is no excuse for such terrible quality), their higher end model is fine.

Oct 30, 2025 12:50 PM in response to HWTech

Yes, redundancy is necessary in case SSD(s) fail quickly. So even though they are pretty reliable, I still have multiple manners so as to spread the risk.


I went with a bit different approach for the NAS'. I put a Raspberry Pi on the network with an SMB share advertised as a TimeMachine destination. This SMB has a mount point that is actually an SMB share from a NAS that does not support advertising its SMBs as TimeMachine destinations. This does route the TM backup in and out of the PI through a single interface but the write speed of the RAID 10 (8 disk) NAS' are the limiting factor so the performance is no worse than direct to the NAS'.


Then I also have three external SSDs; one with direct manual executed TM backup and two with selected home directory manual rsync scripts.


All good now and I get to keep all my old hardware (except the Time Capsules) in place. Should be good until zombie apocalypse.

This thread has been closed by the system or the community team. You may vote for any posts you find helpful, or search the Community for additional answers.

Run bash (.sh) via launchd using a plist in /Library/LaunchAgents

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple Account.