<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Arjun Adhikari</title>
    <description>The latest articles on DEV Community by Arjun Adhikari (@arjun_adhikari_4ac4ca1052).</description>
    <link>https://dev.to/arjun_adhikari_4ac4ca1052</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3950472%2Fbf530c4f-3c9c-4112-b81b-3113d4f2974e.png</url>
      <title>DEV Community: Arjun Adhikari</title>
      <link>https://dev.to/arjun_adhikari_4ac4ca1052</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arjun_adhikari_4ac4ca1052"/>
    <language>en</language>
    <item>
      <title>Cron Not Working on Mac? How to Fix the macOS Sleep Trap with launchd</title>
      <dc:creator>Arjun Adhikari</dc:creator>
      <pubDate>Mon, 25 May 2026 10:35:36 +0000</pubDate>
      <link>https://dev.to/arjun_adhikari_4ac4ca1052/cron-not-working-on-mac-how-to-fix-the-macos-sleep-trap-with-launchd-3l4g</link>
      <guid>https://dev.to/arjun_adhikari_4ac4ca1052/cron-not-working-on-mac-how-to-fix-the-macos-sleep-trap-with-launchd-3l4g</guid>
      <description>&lt;p&gt;If you've ever built an automated script like a Python crypto price scraper, a daily backup script, or an RSS archiver, you probably felt like a genius right up until the moment it failed to run. &lt;/p&gt;

&lt;p&gt;You set up your &lt;code&gt;crontab&lt;/code&gt; perfectly. You told it to run every day at 6:30 PM. You tested the script in your terminal, and it worked flawlessly. But then you check your logs a few days later, and... &lt;em&gt;crickets&lt;/em&gt;. Nothing happened.&lt;/p&gt;

&lt;p&gt;What gives?&lt;/p&gt;

&lt;p&gt;If you are running your automation on a personal Mac, you've just run into the classic &lt;strong&gt;Sleep Mode Trap&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Your Mac is Ignoring Your Cron Jobs
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;cron&lt;/code&gt; is a legendary piece of software. It has been the backbone of Unix automation for decades. But &lt;code&gt;cron&lt;/code&gt; was designed for servers that live in cold, dark rooms and literally never go to sleep.&lt;/p&gt;

&lt;p&gt;Your Mac is different. When you close the lid or walk away for an hour, your Mac goes to sleep to save battery.&lt;/p&gt;

&lt;p&gt;Here is how &lt;code&gt;cron&lt;/code&gt; handles sleep: &lt;strong&gt;It doesn't.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you have a job scheduled for 6:30 PM and your laptop is asleep at 6:30 PM, &lt;code&gt;cron&lt;/code&gt; simply shrugs and skips it. It won't try again until tomorrow at 6:30 PM. If your laptop is closed tomorrow at 6:30 PM too? Skipped again. Your "automated" pipeline is now completely broken unless you babysit it and keep your screen awake.&lt;/p&gt;




&lt;p&gt;Apple knew this was a problem, which is why they built &lt;strong&gt;&lt;code&gt;launchd&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;launchd&lt;/code&gt; is the modern, macOS-native replacement for &lt;code&gt;cron&lt;/code&gt; and &lt;code&gt;init&lt;/code&gt;. It manages everything running in the background on your Mac. Unlike &lt;code&gt;cron&lt;/code&gt;, &lt;code&gt;launchd&lt;/code&gt; is generally much better at handling sleep/wake scenarios — it can run missed scheduled jobs shortly after wake, depending on your exact power state and configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cron vs. launchd at a Glance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;&lt;code&gt;cron&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;launchd&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Handles sleep well&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Native macOS support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Simple syntax&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Good for servers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Good for personal laptops&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  LaunchAgents vs. LaunchDaemons: What's the Difference?
&lt;/h3&gt;

&lt;p&gt;Before writing your configuration, it's crucial to know where to put it. &lt;code&gt;launchd&lt;/code&gt; separates jobs into two categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;LaunchDaemons (&lt;code&gt;/Library/LaunchDaemons/&lt;/code&gt;)&lt;/strong&gt; — Run as the &lt;code&gt;root&lt;/code&gt; system user as soon as the Mac boots, even before anyone logs in. Use this for system-level services like database servers or network listeners.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LaunchAgents (&lt;code&gt;~/Library/LaunchAgents/&lt;/code&gt;)&lt;/strong&gt; — Run as &lt;em&gt;your specific user account&lt;/em&gt;, only after you log in. Since personal scripts need access to your files, home directory, and user permissions, &lt;strong&gt;LaunchAgents are almost always what you want.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  How &lt;code&gt;launchd&lt;/code&gt; Actually Works Under the Hood
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;launchd&lt;/code&gt; is the absolute core of macOS. When you turn on your Mac, &lt;code&gt;launchd&lt;/code&gt; is the very first process that starts — it has Process ID &lt;code&gt;1&lt;/code&gt;. Every other application on your Mac is technically a "child" of &lt;code&gt;launchd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When your Mac sleeps, the hardware clock keeps ticking. When you open your laptop, the Kernel updates the system time and &lt;code&gt;launchd&lt;/code&gt; checks its internal schedule. If it missed a &lt;code&gt;StartCalendarInterval&lt;/code&gt; job during the sleep window, it triggers the job shortly after wake to catch up.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step-by-Step: Migrating from Cron to launchd
&lt;/h2&gt;

&lt;p&gt;Instead of editing a &lt;code&gt;crontab&lt;/code&gt;, you create a Property List (&lt;code&gt;.plist&lt;/code&gt;) XML configuration file and drop it in &lt;code&gt;~/Library/LaunchAgents/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is a complete, production-ready template for a Python script that runs Monday through Friday at 6:30 PM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code&gt;~/Library/LaunchAgents/com.yourname.dailyscript.plist&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;plist&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- 1. Unique identifier for this job --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;com.yourname.dailyscript&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- 2. Command to run — always use absolute paths for the interpreter AND the script --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/bin/python3&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/yourname/projects/my_script.py&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- 3. Working directory — prevents "file not found" errors inside your script --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;WorkingDirectory&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/yourname/projects&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- 4. Environment variables — launchd starts with a minimal PATH, so declare it explicitly --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;EnvironmentVariables&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;PATH&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- 5. Schedule: Monday (1) through Friday (5) at 18:30 --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StartCalendarInterval&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Weekday&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Hour&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;18&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Minute&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;30&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Weekday&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;2&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Hour&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;18&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Minute&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;30&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Weekday&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;3&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Hour&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;18&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Minute&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;30&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Weekday&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;4&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Hour&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;18&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Minute&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;30&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Weekday&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;5&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Hour&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;18&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Minute&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&amp;lt;integer&amp;gt;&lt;/span&gt;30&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- 6. Persistent log files — use ~/Library/Logs, NOT /tmp (which gets wiped by macOS) --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardOutPath&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/yourname/Library/Logs/dailyscript.out&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardErrorPath&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/yourname/Library/Logs/dailyscript.err&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding the Key Fields
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Label&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique name used to identify and manage the job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProgramArguments&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exact command and arguments — equivalent to typing in your terminal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WorkingDirectory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets the &lt;code&gt;pwd&lt;/code&gt; inside your script; prevents relative path failures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EnvironmentVariables&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Injects env vars — critical because &lt;code&gt;launchd&lt;/code&gt; starts with a barebones &lt;code&gt;$PATH&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StartCalendarInterval&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Defines the schedule; &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;7&lt;/code&gt; are both Sunday&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StandardOutPath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Where &lt;code&gt;print()&lt;/code&gt; and &lt;code&gt;stdout&lt;/code&gt; output goes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StandardErrorPath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Where errors and tracebacks go&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;~/Library/Logs/&lt;/code&gt; instead of &lt;code&gt;/tmp/&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
macOS periodically cleans &lt;code&gt;/tmp/&lt;/code&gt; — sometimes as frequently as on every reboot. If you put your logs there, you could lose them before you ever read them. &lt;code&gt;~/Library/Logs/&lt;/code&gt; is the macOS convention for persistent, user-level logs.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Activating the Job (The Modern Way)
&lt;/h2&gt;

&lt;p&gt;Historically, most tutorials teach &lt;code&gt;launchctl load&lt;/code&gt;, but Apple considers this legacy. The modern, recommended way to register a LaunchAgent for your user session is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Register the job&lt;/span&gt;
launchctl bootstrap gui/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; ~/Library/LaunchAgents/com.yourname.dailyscript.plist

&lt;span class="c"&gt;# To test it immediately without waiting for the scheduled time&lt;/span&gt;
launchctl start com.yourname.dailyscript

&lt;span class="c"&gt;# If you edit the plist, you must unload and reload it&lt;/span&gt;
launchctl bootout gui/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; ~/Library/LaunchAgents/com.yourname.dailyscript.plist
launchctl bootstrap gui/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; ~/Library/LaunchAgents/com.yourname.dailyscript.plist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common &lt;code&gt;launchd&lt;/code&gt; Gotchas
&lt;/h2&gt;

&lt;p&gt;If your script isn't running, it is almost certainly one of these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Barebones &lt;code&gt;$PATH&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;launchd&lt;/code&gt; starts with a minimal environment. If your script calls &lt;code&gt;python&lt;/code&gt;, &lt;code&gt;node&lt;/code&gt;, or any Homebrew tool by name without a full path, it will silently fail. Always declare your &lt;code&gt;PATH&lt;/code&gt; explicitly in &lt;code&gt;EnvironmentVariables&lt;/code&gt;, and on Apple Silicon Macs include &lt;code&gt;/opt/homebrew/bin&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Absolute paths everywhere&lt;/strong&gt; — &lt;code&gt;launchd&lt;/code&gt; does not expand &lt;code&gt;~&lt;/code&gt;. Use &lt;code&gt;/Users/yourname/&lt;/code&gt; literally everywhere — in &lt;code&gt;ProgramArguments&lt;/code&gt;, &lt;code&gt;WorkingDirectory&lt;/code&gt;, and log paths.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scripts fail silently&lt;/strong&gt; — There is no terminal window to show you errors. Always check your log files after the first run:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/Library/Logs/dailyscript.err
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verify the job is registered&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;launchctl list | &lt;span class="nb"&gt;grep &lt;/span&gt;com.yourname.dailyscript
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The three columns are: PID (blank if not running), last exit code (0 = success), and label.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stream live system logs&lt;/strong&gt; for deeper debugging:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;log stream &lt;span class="nt"&gt;--predicate&lt;/span&gt; &lt;span class="s1"&gt;'subsystem == "com.apple.launchd"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  A Quick Note on &lt;code&gt;KeepAlive&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;As you explore &lt;code&gt;launchd&lt;/code&gt; further, you will encounter &lt;code&gt;&amp;lt;key&amp;gt;KeepAlive&amp;lt;/key&amp;gt;&lt;/code&gt;. Setting it to &lt;code&gt;&amp;lt;true/&amp;gt;&lt;/code&gt; tells &lt;code&gt;launchd&lt;/code&gt; to treat your script as a permanent background daemon — if it crashes or exits, &lt;code&gt;launchd&lt;/code&gt; will immediately restart it. This is exactly what you want for a persistent web server, but completely wrong for a daily scraper that is supposed to run once and exit.&lt;/p&gt;




&lt;h2&gt;
  
  
  What About Windows?
&lt;/h2&gt;

&lt;p&gt;Windows has its own built-in equivalent: &lt;strong&gt;Task Scheduler&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To enable the same "catch-up on wake" behavior:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Task Scheduler&lt;/strong&gt; and create a new task.&lt;/li&gt;
&lt;li&gt;Set your trigger (daily, weekly, etc.).&lt;/li&gt;
&lt;li&gt;Go to the &lt;strong&gt;Settings&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;"Run task as soon as possible after a scheduled start is missed"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That single checkbox gives you the same sleep-safe behavior as &lt;code&gt;launchd&lt;/code&gt;'s &lt;code&gt;StartCalendarInterval&lt;/code&gt;. Windows also supports environment variables and working directory settings under the task's &lt;strong&gt;Actions&lt;/strong&gt; configuration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;cron&lt;/code&gt; is a classic for a reason — simple, powerful, and universal. But it was built for servers that never sleep. If you are building personal automation on a laptop, you owe it to yourself to use the scheduler your OS was actually designed around.&lt;/p&gt;

&lt;p&gt;Your data (and your sanity) will thank you.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>productivity</category>
      <category>systems</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
