Run DevThrottle on a Mac unattended
7 min read
A Mac mini makes a great always-on machine for running coding agents - but macOS ships tuned for a person sitting in front of it, not for a machine nobody touches for weeks. This page is the checklist that turns a Mac into an unattended agent machine: what to change, the exact commands, and the tradeoffs stated plainly. (The Mac runs agents as a workstation; the machine that hosts the Gateway is still a Windows PC today.)
Why the screen lock is the enemy
macOS cannot launch a new graphical application while the screen is locked. When the lock engages, the login window takes ownership of the displays. Your session keeps running - applications that are already open keep working, and background processes are unaffected - but any new graphical launch cannot bind display resources at startup and fails. The Director is a graphical application, so on a locked Mac nothing can start a new Director - not a scheduled run, not a remote request, not a restart after an update or crash - until a human unlocks the screen.
This is architectural, not a bug, and there is no setting, entitlement, or launchd trick that gets around it. We proved it on our own hardware (a Director launched into a locked session dies instantly with a render-timer error, even from a launch agent placed directly in the graphical user domain), and it matches what Apple's own engineers say on the developer forums: there is no mechanism for launching an application behind the lock screen. Windows is genuinely different by design - locking a Windows machine just switches which desktop is visible, while the session and its windows stay fully active underneath, so new applications launch fine on a locked Windows PC. That difference is one more reason the always-on gateway machine is a Windows PC today.
The industry already solved this for Macs. Every commercial Mac build farm - GitHub's hosted macOS runners, Cirrus Labs images, Buildkite's Amazon EC2 Mac guide, GitLab's MacStadium images - ships the identical configuration: automatic login, screen lock disabled, screen saver disabled, system sleep disabled, FileVault off. The display may sleep; the session never locks. That is exactly the configuration below.
The security tradeoff, stated plainly
Compensating controls that still work: keep the machine reachable only over a private network or VPN (see answering agents remotely for the reachability picture), keep Screen Sharing and Remote Login password-protected, and remember that Find My can remotely lock a stolen machine after the fact.
The checklist
Everything scriptable is collected in scripts/mac-unattended-setup.sh in the open source repository - it shows the current value of every setting and asks before each change. The same steps, in order, for doing it by hand:
1. Turn FileVault off
Automatic login does not exist while FileVault is on: the encrypted disk needs a typed password before macOS can even start. Decryption runs in the background; the machine stays usable.
sudo fdesetup disableSystem Settings path: Privacy & Security > FileVault.
Tradeoff: the disk is readable if the hardware is stolen. If that risk is unacceptable, keep FileVault on and accept that every reboot needs a person (for a planned reboot, sudo fdesetup authrestart skips the unlock screen exactly once - it does not help with power failures).
2. Enable automatic login
Every restart - planned, power failure, or crash - lands on the logged-in desktop where the Director can start.
sudo sysadminctl -autologin set -userName <user> -password -System Settings path: Users & Groups > Automatically log in as.
Tradeoff: the account password is stored obfuscated (not encrypted) in /etc/kcpassword.
3. Set "require password after sleep or screen saver" to Never
This is the setting that actually prevents locking. With it off, the display turning off no longer locks the session, and new graphical launches keep working around the clock.
sysadminctl -screenLock off -password -System Settings path: Lock Screen > "Require password after screen saver begins or display is turned off" > Never.
Gotcha: if the control is grayed out, open the iPhone Mirroring app and set it to "Ask every time" first - a known macOS quirk.
Note: the old defaults write com.apple.screensaver askForPassword 0 trick stopped working around macOS 13 - sysadminctl is the supported command, and it requires the account password by design.
4. Disable the screen saver
defaults -currentHost write com.apple.screensaver idleTime -int 0
sudo defaults write /Library/Preferences/com.apple.screensaver loginWindowIdleTime -int 0System Settings path: Lock Screen > "Start Screen Saver when inactive" > Never.
5. Power settings: never sleep the machine, let the display sleep
sudo pmset -a sleep 0 # the machine never sleeps
sudo pmset -a disksleep 0 # disks never spin down
sudo pmset -a displaysleep 10 # the display MAY turn off - harmless once the lock is off
sudo pmset -a womp 1 # wake on network access
sudo pmset -a autorestart 1 # start up again after a power failureSystem Settings path: Energy.
Gotcha: some Apple Silicon minis honor "start up after power failure" inconsistently after a hard power cut. Test yours (pull the plug); if it fails, a smart plug that cycles power is the community workaround.
6. Software updates: security automatic, reboots manual
Keep the invisible security pieces automatic, but stop macOS from installing full operating-system updates on its own - those reboot the machine and kill every running session.
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticCheckEnabled -bool true
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticDownload -bool true
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate CriticalUpdateInstall -bool true
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate ConfigDataInstall -bool true
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates -bool falseThen install operating-system updates yourself, on your schedule: sudo softwareupdate -ia during a maintenance window.
Tradeoff: you own the patch schedule now - put a recurring reminder somewhere.
7. Quiet-machine settings
defaults write com.apple.CrashReporter DialogType none # no crash dialogs (crashes still logged)
defaults write NSGlobalDomain NSAppSleepDisabled -bool YES # no App Nap throttling of background apps
sudo defaults write /Library/Preferences/com.apple.TimeMachine DoNotOfferNewDisksForBackup -bool true8. Enable Remote Login for recovery
When the graphical session is unreachable, ssh over your private network is how you fix the machine without walking to it. Screen Sharing is the second door - keep both.
sudo systemsetup -setremotelogin on9. Privacy permissions: the one-time clicks
The high-value privacy permissions live in a database protected by System Integrity Protection. No script, no command line, no setting can grant them on a normal machine - only a human clicking in System Settings (or mobile device management on a supervised fleet). The good news: a grant is inherited by every process the granted application launches, so granting the Director covers all the agent processes it spawns. Grant once, done forever - with one caveat: the grant is tied to the application's code signature, so it sticks for the installed app, but a rebuilt ad-hoc-signed development build counts as a new application.
| Permission | Grant it to | Needed for |
|---|---|---|
| Full Disk Access | The Director (and your terminal, if you launch agents from one) | Agents reading anywhere on disk without per-folder prompts |
| Screen Recording | The Director | The screenshot features |
| Automation | Approve prompts as they appear | Controlling other applications |
System Settings path: Privacy & Security > (each category) > add the app with the + button.
What you do NOT need to change
- Gatekeeper - locally built binaries never get the quarantine flag, so Gatekeeper never prompts for them. Leave Gatekeeper on; do not use
spctl --global-disable. - The application firewall - if it is off, there are no "accept incoming connections?" prompts. If you turn it on, know that ad-hoc-signed binaries get a fresh signature every rebuild, so each rebuild re-prompts; the fix is signing local builds with a stable self-signed certificate, or scripting
socketfilterfw --add --unblockappinto the build. - Notifications - banners never block a background process; they only matter to a person looking at the screen.
Verify the setup
- Restart the Mac. It must land on the desktop with nobody touching it.
- Turn the display off (
pmset displaysleepnow), wait a minute, then start a new agent session on this machine remotely. It must launch cleanly. - Pull the power cord for ten seconds. The Mac should boot itself back to the desktop.
- From another machine:
sshworks and Screen Sharing connects.
How the rest of the community handles this
You are not alone in this configuration - it is what everyone running unattended Macs converges on:
- Mac build farms (GitHub's hosted macOS runners, Cirrus Labs images, GitLab's MacStadium images, Buildkite's Amazon EC2 Mac guide) all ship automatic login with the lock, screen saver, and sleep disabled. Their provisioning scripts run the same commands listed above.
- The OpenClaw community - thousands of people hosting the open-source agent on dedicated Mac minis - independently arrived at the identical recipe: automatic login, sleep off, lock and screen saver set to Never, "start up after power failure" on, and the FileVault tradeoff documented just as bluntly as here. Their guides add one more trick for Macs with no monitor: an HDMI dummy plug, because macOS graphical rendering and screen capture misbehave with no display attached at all. If your Mac mini has no monitor, buy the dummy plug.
Browsers deserve a special note, because agents drive them constantly:
- Headless browsers are immune. Headless Chromium (Playwright, Puppeteer) never talks to the display, so it launches and runs fine regardless of lock state or missing monitors.
- Headed browsers degrade rather than crash. A visible browser window on a locked or backgrounded Mac gets its rendering and timers throttled - automation pauses and resumes on unlock. Browser-automation people work around it with launch flags (
--disable-background-timer-throttling --disable-renderer-backgrounding), but on a never-locked machine the problem does not arise. - Attaching to an already-running browser keeps working while locked, because the remote-debugging protocol is a plain network interface and its screenshots come from the browser's own compositor, not the physical screen. This matches the general rule: existing graphical processes survive a lock; only new display-bound launches fail.
- There is no virtual-display server for macOS the way Linux has one for X11 - a dummy plug or virtual-display utility is the closest substitute, and neither bypasses the lock screen.
Where to go next
- What the Gateway does - why the always-on hub is a Windows machine, and how a Mac fits in as a workstation.
- Scheduled runs (cron) - unattended agent runs on a schedule.
- Answering agents remotely - reaching the board from another machine.