Practical scripts and templates for running AI workloads as always-on macOS daemons.
Companion to My OpenClaw chronicles — What running local AI on a Mac Mini actually taught me: the 7 infrastructure walls I hit running a local AI shadow-testing system 24/7 on a Mac Mini M4, and the specific fixes for each one.
MIT licensed. Use freely, adapt as needed.
| File | Fixes | What it does |
|---|---|---|
plists/caffeinate.plist |
Gotcha #1 | Prevents macOS from sleeping when idle — keeps the network alive |
scripts/inject-env.sh |
Gotcha #2 | Bakes env vars directly into a LaunchAgent plist (setenv doesn't work) |
scripts/install-github-release.sh |
Gotcha #3 | Downloads the latest release binary from any GitHub repo into ~/.local/bin |
scripts/check-daemon-script.sh |
Gotcha #6 | Validates a Python script before deploying as a LaunchAgent |
templates/daemon_template.py |
Gotcha #6 | Python daemon starter with correct startup logging |
templates/embedding_store_template.py |
Gotcha #7 | Embedding store with fallback backend tagging for evaluation pipelines |
cp plists/caffeinate.plist ~/Library/LaunchAgents/com.local.caffeinate.plist
launchctl load ~/Library/LaunchAgents/com.local.caffeinate.plistVerify it's working:
pmset -g assertions | grep caffeinate
# Should show: "THE CAFFEINATE TOOL IS PREVENTING SLEEP."chmod +x scripts/inject-env.sh
scripts/inject-env.sh ~/Library/LaunchAgents/my.service.plist API_TOKEN "sk-abc123"chmod +x scripts/install-github-release.sh
scripts/install-github-release.sh jqlang/jq "jq-macos-arm64" ~/.local/binchmod +x scripts/check-daemon-script.sh
scripts/check-daemon-script.sh my_daemon.pycp templates/daemon_template.py my_daemon.py
# Edit my_daemon.py, add your logic in run()
scripts/check-daemon-script.sh my_daemon.py # validate firstThese are documented in detail in the blog post. Short version:
- macOS sleeps by default —
caffeinate.plistprevents it permanently launchctl setenvdoesn't affect LaunchAgents —inject-env.shbakes vars into the plist directly- Non-admin accounts can't use Homebrew —
install-github-release.shbypasses it for most CLI tools - Node.js binary path changes silently revoke TCC permissions — no script fix, just don't do it; covered in README only
- Python 3.14 breaks Pydantic V1 — check your deps before committing to a Python version
- Syntax errors in daemon scripts fail silently —
check-daemon-script.shanddaemon_template.pycatch this before it costs you hours - Free-tier embedding APIs corrupt evaluation data without tagging —
embedding_store_template.pyshows the tagging pattern
- macOS (Apple Silicon or Intel)
bash(built-in)curl(built-in)jqforinstall-github-release.sh— install it first using the script itself:curl -L https://github.com/jqlang/jq/releases/latest/download/jq-macos-arm64 \ -o ~/.local/bin/jq && chmod +x ~/.local/bin/jq
- Python 3.x for the Python templates
MIT — see LICENSE.