OpenClaw macOS Always-On
by @happydog-intj
Run OpenClaw as a system-level LaunchDaemon on macOS for 24/7 operation, surviving screen lock, logout, and user switching. Uses caffeinate to prevent sleep....
clawhub install openclaw-macos-always-onπ About This Skill
name: macos-launchdaemon description: Run OpenClaw as a system-level LaunchDaemon on macOS for 24/7 operation, surviving screen lock, logout, and user switching. Uses caffeinate to prevent sleep. Verified working for long-term locked screen scenarios.
macOS LaunchDaemon Setup for OpenClaw
Run OpenClaw as a system-level service (LaunchDaemon) with caffeinate to ensure 24/7 operation. This configuration has been tested and verified to work even after extended screen lock periods (30+ minutes).
Ensures OpenClaw continues running when:
When to Use This
Use LaunchDaemon if:
Use LaunchAgent if:
Quick Setup
One-Command Installation
curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/clawd/master/skills/macos-launchdaemon/install.sh | bash
Or manual setup below β¬οΈ
Manual Setup
Step 1: Create LaunchDaemon Configuration
Create the plist file with your actual username. This configuration uses caffeinate and has been tested and verified to work even after 30+ minutes of screen lock:
cat > /tmp/ai.openclaw.gateway.plist << 'EOF'
Label
ai.openclaw.gateway
Comment
OpenClaw Gateway (System Daemon - Network Always On)
RunAtLoad
KeepAlive
SuccessfulExit
NetworkState
Crashed
ThrottleInterval
0
ProcessType
Interactive
EnableTransactions
UserName
YOUR_USERNAME
GroupName
staff
ProgramArguments
/usr/bin/caffeinate
-s
/opt/homebrew/bin/node
/opt/homebrew/lib/node_modules/openclaw/dist/index.js
gateway
--port
18789
StandardOutPath
/Users/YOUR_USERNAME/.openclaw/logs/gateway.log
StandardErrorPath
/Users/YOUR_USERNAME/.openclaw/logs/gateway.err.log
EnvironmentVariables
HOME
/Users/YOUR_USERNAME
PATH
/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin
EOFReplace YOUR_USERNAME with your actual username
sed -i '' "s/YOUR_USERNAME/$(whoami)/g" /tmp/ai.openclaw.gateway.plist
π Key Configuration for Lock Screen Persistence:
/usr/bin/caffeinate -s - Prevents system sleep (display can sleep, network stays active)NetworkState: true - Ensures service stays active when network is availableProcessType: Interactive - Prevents macOS from suspending the processThrottleInterval: 0 - Disables any throttlingCrashed: true - Auto-restarts on crashesβ Verified working after 30+ minutes of screen lock on macOS 14.4
Step 2: Stop Existing LaunchAgent
# Stop user-level service
launchctl bootout gui/$(id -u)/ai.openclaw.gateway 2>/dev/nullBackup and disable LaunchAgent plist
mv ~/Library/LaunchAgents/ai.openclaw.gateway.plist ~/Library/LaunchAgents/ai.openclaw.gateway.plist.disabled 2>/dev/null
Step 3: Install LaunchDaemon (requires sudo)
# Copy to system location
sudo cp /tmp/ai.openclaw.gateway.plist /Library/LaunchDaemons/Set correct permissions
sudo chown root:wheel /Library/LaunchDaemons/ai.openclaw.gateway.plist
sudo chmod 644 /Library/LaunchDaemons/ai.openclaw.gateway.plistLoad and start service
sudo launchctl bootstrap system /Library/LaunchDaemons/ai.openclaw.gateway.plist
Step 4: Verify Installation
# Check process is running
ps aux | grep openclaw-gateway | grep -v grepVerify PPID = 1 (launched by system launchd)
ps -p $(pgrep -f openclaw-gateway) -o pid,ppid,user,commandCheck service status
sudo launchctl print system/ai.openclaw.gateway | head -10Test with OpenClaw
openclaw status
Expected output:
PID PPID USER COMMAND
12345 1 youruser openclaw-gateway
PPID=1 confirms it's running as LaunchDaemon (parent is system launchd).
Testing Lock Screen Behavior
Test Script
#!/bin/bash
echo "π§ͺ Testing LaunchDaemon lock screen behavior..."
echo ""
echo "1. Lock your Mac in 5 seconds..."
sleep 5
pmset displaysleepnowecho "2. Use your phone to send 'ping' to your bot"
echo "3. Bot should reply 'pong! π' even while locked"
echo ""
echo "Unlock your Mac after testing."
Manual Test Steps
1. Lock your Mac: β + Control + Q 2. Send from phone: Message your bot (Telegram/Feishu/etc.) with "ping" 3. Expected: Bot replies "pong! π" immediately 4. Unlock and verify logs show the message was processed
Management Commands
View Logs
# Real-time logs
tail -f ~/.openclaw/logs/gateway.logError logs
tail -f ~/.openclaw/logs/gateway.err.logLast 100 lines
tail -100 ~/.openclaw/logs/gateway.log
Restart Service
# Unload and reload
sudo launchctl bootout system/ai.openclaw.gateway
sudo launchctl bootstrap system /Library/LaunchDaemons/ai.openclaw.gateway.plistOr use kickstart (restarts without unloading)
sudo launchctl kickstart -k system/ai.openclaw.gateway
Stop Service
# Stop service
sudo launchctl bootout system/ai.openclaw.gatewayPrevent auto-start
sudo launchctl disable system/ai.openclaw.gateway
Start Service
# Enable and start
sudo launchctl enable system/ai.openclaw.gateway
sudo launchctl bootstrap system /Library/LaunchDaemons/ai.openclaw.gateway.plist
Check Status
# Full service details
sudo launchctl print system/ai.openclaw.gatewayQuick status check
sudo launchctl list | grep openclawProcess info
ps aux | grep openclaw-gateway | grep -v grep
Uninstallation
Complete Removal
# 1. Stop service
sudo launchctl bootout system/ai.openclaw.gateway2. Remove plist
sudo rm /Library/LaunchDaemons/ai.openclaw.gateway.plist3. Restore LaunchAgent (optional)
mv ~/Library/LaunchAgents/ai.openclaw.gateway.plist.disabled ~/Library/LaunchAgents/ai.openclaw.gateway.plist 2>/dev/null
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist4. Verify
ps aux | grep openclaw | grep -v grep
Automated Install Script
Save this as install-launchdaemon.sh:
#!/bin/bash
set -eecho "π OpenClaw LaunchDaemon Installer"
echo "=================================="
echo ""
Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No ColorUSERNAME=$(whoami)
PLIST_PATH="/Library/LaunchDaemons/ai.openclaw.gateway.plist"
TEMP_PLIST="/tmp/ai.openclaw.gateway.plist"
Check if running as root
if [ "$EUID" -eq 0 ]; then
echo -e "${RED}β Don't run this script with sudo${NC}"
echo "The script will ask for sudo password when needed."
exit 1
fiCheck if OpenClaw is installed
if ! command -v openclaw &> /dev/null; then
echo -e "${RED}β OpenClaw not found. Install it first:${NC}"
echo " npm install -g openclaw"
exit 1
fiecho -e "${YELLOW}π Creating LaunchDaemon configuration...${NC}"
Get OpenClaw gateway token
GATEWAY_TOKEN=$(openclaw config get gateway.auth.token 2>/dev/null | tr -d '"' || echo "")cat > "$TEMP_PLIST" << EOF
Label
ai.openclaw.gateway
Comment
OpenClaw Gateway (System Daemon)
RunAtLoad
KeepAlive
UserName
$USERNAME
GroupName
staff
ProgramArguments
/opt/homebrew/bin/node
/opt/homebrew/lib/node_modules/openclaw/dist/index.js
gateway
--port
18789
StandardOutPath
/Users/$USERNAME/.openclaw/logs/gateway.log
StandardErrorPath
/Users/$USERNAME/.openclaw/logs/gateway.err.log
EnvironmentVariables
HOME
/Users/$USERNAME
PATH
/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin
OPENCLAW_GATEWAY_PORT
18789
OPENCLAW_GATEWAY_TOKEN
$GATEWAY_TOKEN
EOF
echo -e "${YELLOW}π Stopping existing services...${NC}"
Stop LaunchAgent
launchctl bootout gui/$(id -u)/ai.openclaw.gateway 2>/dev/null || trueBackup LaunchAgent plist
if [ -f ~/Library/LaunchAgents/ai.openclaw.gateway.plist ]; then
mv ~/Library/LaunchAgents/ai.openclaw.gateway.plist ~/Library/LaunchAgents/ai.openclaw.gateway.plist.backup
echo -e "${GREEN}β
Backed up LaunchAgent plist${NC}"
fiStop existing LaunchDaemon
sudo launchctl bootout system/ai.openclaw.gateway 2>/dev/null || trueecho -e "${YELLOW}π¦ Installing LaunchDaemon...${NC}"
Install plist
sudo cp "$TEMP_PLIST" "$PLIST_PATH"
sudo chown root:wheel "$PLIST_PATH"
sudo chmod 644 "$PLIST_PATH"echo -e "${YELLOW}π Starting service...${NC}"
Start service
sudo launchctl bootstrap system "$PLIST_PATH"
sleep 3Verify
if ps aux | grep -q "[o]penclaw-gateway"; then
echo ""
echo -e "${GREEN}β
LaunchDaemon installed successfully!${NC}"
echo ""
echo "π Service Status:"
ps aux | grep "[o]penclaw-gateway" | awk '{print " PID: "$2", User: "$1}'
echo ""
echo "π§ͺ Test it:"
echo " 1. Lock your Mac: β + Control + Q"
echo " 2. Send 'ping' from your phone"
echo " 3. Bot should reply even while locked!"
echo ""
echo "π Management:"
echo " Logs: tail -f ~/.openclaw/logs/gateway.log"
echo " Restart: sudo launchctl kickstart -k system/ai.openclaw.gateway"
echo " Stop: sudo launchctl bootout system/ai.openclaw.gateway"
echo " Status: sudo launchctl print system/ai.openclaw.gateway"
else
echo -e "${RED}β Service failed to start${NC}"
echo "Check logs: tail -50 ~/.openclaw/logs/gateway.err.log"
exit 1
fi
Make it executable:
chmod +x install-launchdaemon.sh
./install-launchdaemon.sh
Troubleshooting
Service Won't Start
Check logs:
tail -50 ~/.openclaw/logs/gateway.err.log
Common issues:
1. Wrong username in plist
# Verify username matches
grep UserName /Library/LaunchDaemons/ai.openclaw.gateway.plist
whoami
2. Wrong node path
# Check node location
which node
# Update plist if needed (change /opt/homebrew/bin/node to your path)
3. Permissions issues
# Fix log directory permissions
mkdir -p ~/.openclaw/logs
chmod 755 ~/.openclaw/logs
Still Suspends After Lock
If using older macOS or specific hardware:
# Prevent system sleep
sudo pmset -a sleep 0
sudo pmset -a disksleep 0
sudo pmset -a displaysleep 10 # Screen off but system awake
Or use caffeinate (not recommended for laptops):
# Modify ProgramArguments in plist to wrap with caffeinate
/usr/bin/caffeinate
-s
/opt/homebrew/bin/node
...
Port Already in Use
# Find what's using port 18789
lsof -i :18789Kill the process
kill -9 Or change port in config and plist
openclaw config set gateway.port 18790
Logs Not Writing
# Create log directory
mkdir -p ~/.openclaw/logsTest permissions
touch ~/.openclaw/logs/test.log
ls -la ~/.openclaw/logs/Check plist paths match
grep Path /Library/LaunchDaemons/ai.openclaw.gateway.plist
Security Considerations
Running as User vs Root
β
This setup runs as your user (specified in )
File Permissions
# LaunchDaemon plist should be owned by root
ls -l /Library/LaunchDaemons/ai.openclaw.gateway.plist
Should show: -rw-r--r-- 1 root wheel
Log directory owned by you
ls -ld ~/.openclaw/logs
Should show: drwxr-xr-x ... youruser staff
Token Security
The Gateway token is stored in the plist environment variables. While readable only by root and your user, consider:
# Check who can read the plist
ls -l /Library/LaunchDaemons/ai.openclaw.gateway.plistMore secure: use macOS Keychain (advanced)
Store token in keychain and retrieve at runtime
Performance Impact
LaunchDaemon has minimal performance impact:
macOS Version Compatibility
Tested on:
Note: LaunchDaemon syntax changed slightly in macOS 11+, but backwards compatible.
Comparison: LaunchAgent vs LaunchDaemon
| Feature | LaunchAgent | LaunchDaemon | |---------|-------------|--------------| | Runs when locked | β May suspend | β Always runs | | Runs when logged out | β Stops | β Continues | | Setup complexity | Simple | Requires sudo | | Requires sudo | β No | β Yes | | Best for | Personal use, logged-in only | 24/7 server, multi-user | | Security | User-level | System-level (still runs as user) | | Auto-start | At login | At boot |
Migration
From LaunchAgent to LaunchDaemon
Use the install script above, or:
# Automatic migration
launchctl bootout gui/$(id -u)/ai.openclaw.gateway
mv ~/Library/LaunchAgents/ai.openclaw.gateway.plist ~/Library/LaunchAgents/ai.openclaw.gateway.plist.backup
Then follow installation steps
From LaunchDaemon back to LaunchAgent
# Stop daemon
sudo launchctl bootout system/ai.openclaw.gateway
sudo rm /Library/LaunchDaemons/ai.openclaw.gateway.plistRestore agent
mv ~/Library/LaunchAgents/ai.openclaw.gateway.plist.backup ~/Library/LaunchAgents/ai.openclaw.gateway.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist
FAQ
Q: Will this drain my battery? A: Minimal impact. OpenClaw idles at <1% CPU when not processing messages.
Q: Can I still update OpenClaw? A: Yes. After updating, restart the service:
sudo launchctl kickstart -k system/ai.openclaw.gateway
Q: What if I upgrade macOS? A: LaunchDaemon survives OS upgrades. Verify it's still running after update:
sudo launchctl print system/ai.openclaw.gateway
Q: Can I run multiple instances? A: Not recommended. Use one LaunchDaemon with multiple channel accounts instead.
Q: Does this work on M1/M2 Macs? A: Yes! Works on both Intel and Apple Silicon Macs.
Related Skills
Contributing
Found issues or improvements?
Quick Commands Reference:
# Status
sudo launchctl print system/ai.openclaw.gatewayRestart
sudo launchctl kickstart -k system/ai.openclaw.gatewayLogs
tail -f ~/.openclaw/logs/gateway.logUninstall
sudo launchctl bootout system/ai.openclaw.gateway
sudo rm /Library/LaunchDaemons/ai.openclaw.gateway.plist
π Tips & Best Practices
Service Won't Start
Check logs:
tail -50 ~/.openclaw/logs/gateway.err.log
Common issues:
1. Wrong username in plist
# Verify username matches
grep UserName /Library/LaunchDaemons/ai.openclaw.gateway.plist
whoami
2. Wrong node path
# Check node location
which node
# Update plist if needed (change /opt/homebrew/bin/node to your path)
3. Permissions issues
# Fix log directory permissions
mkdir -p ~/.openclaw/logs
chmod 755 ~/.openclaw/logs
Still Suspends After Lock
If using older macOS or specific hardware:
# Prevent system sleep
sudo pmset -a sleep 0
sudo pmset -a disksleep 0
sudo pmset -a displaysleep 10 # Screen off but system awake
Or use caffeinate (not recommended for laptops):
# Modify ProgramArguments in plist to wrap with caffeinate
/usr/bin/caffeinate
-s
/opt/homebrew/bin/node
...
Port Already in Use
# Find what's using port 18789
lsof -i :18789Kill the process
kill -9 Or change port in config and plist
openclaw config set gateway.port 18790
Logs Not Writing
# Create log directory
mkdir -p ~/.openclaw/logsTest permissions
touch ~/.openclaw/logs/test.log
ls -la ~/.openclaw/logs/Check plist paths match
grep Path /Library/LaunchDaemons/ai.openclaw.gateway.plist