openclaw-local-mac-mini
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOpenClaw Local + Mac mini Setup
OpenClaw 本地部署 + Mac mini 配置指南
Use this skill when you want to run OpenClaw on a developer laptop or promote it to a stable Mac mini host. Covers cloning and bootstrapping, Docker Compose configuration, Mac mini hardware optimization, networking, monitoring, and production-grade launchd services.
当你想要在开发笔记本电脑上运行OpenClaw,或是将其部署到稳定的Mac mini主机上时,可以参考本指南。内容涵盖克隆与初始化、Docker Compose配置、Mac mini硬件优化、网络设置、监控以及生产级别的launchd服务配置。
When to Use
适用场景
- Running OpenClaw as a private, always-on local AI agent
- Setting up a dedicated Mac mini as a home-lab AI server
- Deploying OpenClaw with Docker Compose for reproducible environments
- Optimizing macOS for headless server operation
- Monitoring a local AI service for uptime and performance
- 将OpenClaw作为私密、持续在线的本地AI Agent运行
- 将专用Mac mini设置为家庭实验室AI服务器
- 使用Docker Compose部署OpenClaw以实现可复现的环境
- 优化macOS以支持无头服务器运行
- 监控本地AI服务的可用性与性能
Prerequisites
前置条件
- macOS 13 (Ventura) or later on Apple Silicon (M1/M2/M4 Mac mini recommended)
- Docker Desktop for Mac or OrbStack installed
- Git, Node.js (v18+), and a package manager (npm or pnpm)
- API keys for your chosen LLM provider (OpenAI, Anthropic, or local Ollama)
- At least 16 GB RAM (32 GB recommended for local model serving)
- 搭载Apple Silicon(推荐M1/M2/M4 Mac mini)且运行macOS 13(Ventura)或更高版本的设备
- 已安装Docker Desktop for Mac或OrbStack
- 已安装Git、Node.js(v18+)以及包管理器(npm或pnpm)
- 所选LLM提供商的API密钥(OpenAI、Anthropic或本地Ollama)
- 至少16GB内存(本地模型服务推荐32GB)
Local Setup (Any Dev Machine)
本地搭建(适用于任意开发设备)
Clone and Bootstrap
克隆与初始化
bash
undefinedbash
undefinedClone the repository
Clone the repository
git clone https://github.com/openclaw/openclaw.git
cd openclaw
git clone https://github.com/openclaw/openclaw.git
cd openclaw
Review the upstream README for current prerequisites
Review the upstream README for current prerequisites
cat README.md
cat README.md
Copy the example environment file
Copy the example environment file
cp .env.example .env
cp .env.example .env
Edit .env with your provider keys and configuration
Edit .env with your provider keys and configuration
At minimum, set the model provider and API key
At minimum, set the model provider and API key
cat > .env << 'ENV'
cat > .env << 'ENV'
LLM Provider Configuration
LLM Provider Configuration
OPENAI_API_KEY=sk-your-openai-key-here
OPENAI_API_KEY=sk-your-openai-key-here
Or for Anthropic:
Or for Anthropic:
ANTHROPIC_API_KEY=sk-ant-your-key-here
ANTHROPIC_API_KEY=sk-ant-your-key-here
Or for local Ollama:
Or for local Ollama:
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_BASE_URL=http://localhost:11434
Application settings
Application settings
NODE_ENV=development
PORT=3000
HOST=0.0.0.0
LOG_LEVEL=info
NODE_ENV=development
PORT=3000
HOST=0.0.0.0
LOG_LEVEL=info
Database (if applicable)
Database (if applicable)
DATABASE_URL=sqlite:./data/openclaw.db
ENV
undefinedDATABASE_URL=sqlite:./data/openclaw.db
ENV
undefinedInstall Dependencies and Run
安装依赖并运行
bash
undefinedbash
undefinedInstall dependencies
Install dependencies
npm install
npm install
Or with pnpm:
Or with pnpm:
pnpm install
pnpm install
Run database migrations if needed
Run database migrations if needed
npm run db:migrate
npm run db:migrate
Start the development server
Start the development server
npm run dev
npm run dev
Verify startup
Verify startup
curl -s http://localhost:3000/api/health | jq .
curl -s http://localhost:3000/api/health | jq .
Expected: {"status":"ok","version":"..."}
Expected: {"status":"ok","version":"..."}
undefinedundefinedValidate the Setup
验证搭建结果
bash
undefinedbash
undefinedCheck the API health endpoint
Check the API health endpoint
curl -f http://localhost:3000/api/health
curl -f http://localhost:3000/api/health
Check the UI loads
Check the UI loads
curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/
curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/
Expected: 200
Expected: 200
Run built-in tests if available
Run built-in tests if available
npm test
undefinednpm test
undefinedDocker Compose Setup
Docker Compose 配置
docker-compose.yml
docker-compose.yml
yaml
version: "3.8"
services:
openclaw:
build:
context: .
dockerfile: Dockerfile
image: openclaw:latest
container_name: openclaw
restart: unless-stopped
ports:
- "3000:3000"
env_file:
- .env
environment:
- NODE_ENV=production
- HOST=0.0.0.0
- PORT=3000
volumes:
- openclaw-data:/app/data
- ./config:/app/config:ro
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
deploy:
resources:
limits:
memory: 4G
reservations:
memory: 1G
logging:
driver: json-file
options:
max-size: "50m"
max-file: "5"
# Optional: Redis for caching/queues
redis:
image: redis:7-alpine
container_name: openclaw-redis
restart: unless-stopped
volumes:
- redis-data:/data
command: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
# Optional: Ollama for local model serving
ollama:
image: ollama/ollama:latest
container_name: openclaw-ollama
restart: unless-stopped
ports:
- "11434:11434"
volumes:
- ollama-models:/root/.ollama
deploy:
resources:
limits:
memory: 16G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
interval: 30s
timeout: 10s
retries: 3
volumes:
openclaw-data:
redis-data:
ollama-models:yaml
version: "3.8"
services:
openclaw:
build:
context: .
dockerfile: Dockerfile
image: openclaw:latest
container_name: openclaw
restart: unless-stopped
ports:
- "3000:3000"
env_file:
- .env
environment:
- NODE_ENV=production
- HOST=0.0.0.0
- PORT=3000
volumes:
- openclaw-data:/app/data
- ./config:/app/config:ro
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
deploy:
resources:
limits:
memory: 4G
reservations:
memory: 1G
logging:
driver: json-file
options:
max-size: "50m"
max-file: "5"
# Optional: Redis for caching/queues
redis:
image: redis:7-alpine
container_name: openclaw-redis
restart: unless-stopped
volumes:
- redis-data:/data
command: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
# Optional: Ollama for local model serving
ollama:
image: ollama/ollama:latest
container_name: openclaw-ollama
restart: unless-stopped
ports:
- "11434:11434"
volumes:
- ollama-models:/root/.ollama
deploy:
resources:
limits:
memory: 16G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
interval: 30s
timeout: 10s
retries: 3
volumes:
openclaw-data:
redis-data:
ollama-models:Running with Docker Compose
使用Docker Compose运行
bash
undefinedbash
undefinedBuild and start all services
Build and start all services
docker compose up -d --build
docker compose up -d --build
Check service status
Check service status
docker compose ps
docker compose ps
View logs
View logs
docker compose logs -f openclaw
docker compose logs -f --tail=100 ollama
docker compose logs -f openclaw
docker compose logs -f --tail=100 ollama
Pull a model into Ollama (if using local models)
Pull a model into Ollama (if using local models)
docker exec openclaw-ollama ollama pull llama3:8b
docker exec openclaw-ollama ollama list
docker exec openclaw-ollama ollama pull llama3:8b
docker exec openclaw-ollama ollama list
Restart a single service
Restart a single service
docker compose restart openclaw
docker compose restart openclaw
Stop everything
Stop everything
docker compose down
docker compose down
Stop and remove volumes (full reset)
Stop and remove volumes (full reset)
docker compose down -v
undefineddocker compose down -v
undefinedMac mini Production Setup
Mac mini 生产环境配置
macOS Hardening and Baseline
macOS 加固与基础配置
bash
undefinedbash
undefinedEnable automatic security updates
Enable automatic security updates
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 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
Enable FileVault disk encryption
Enable FileVault disk encryption
sudo fdesetup enable
sudo fdesetup enable
Disable sleep (headless server should never sleep)
Disable sleep (headless server should never sleep)
sudo pmset -a sleep 0
sudo pmset -a disksleep 0
sudo pmset -a displaysleep 0
sudo pmset -a sleep 0
sudo pmset -a disksleep 0
sudo pmset -a displaysleep 0
Enable auto-restart after power failure
Enable auto-restart after power failure
sudo pmset -a autorestart 1
sudo pmset -a autorestart 1
Disable screen saver
Disable screen saver
defaults -currentHost write com.apple.screensaver idleTime 0
defaults -currentHost write com.apple.screensaver idleTime 0
Set hostname
Set hostname
sudo scutil --set ComputerName "openclaw-mini"
sudo scutil --set HostName "openclaw-mini"
sudo scutil --set LocalHostName "openclaw-mini"
sudo scutil --set ComputerName "openclaw-mini"
sudo scutil --set HostName "openclaw-mini"
sudo scutil --set LocalHostName "openclaw-mini"
Verify power settings
Verify power settings
pmset -g
undefinedpmset -g
undefinedDedicated User Account
创建专用用户账户
bash
undefinedbash
undefinedCreate a dedicated service user
Create a dedicated service user
sudo sysadminctl -addUser openclaw -fullName "OpenClaw Service" -password "temp-change-me" -admin
sudo sysadminctl -addUser openclaw -fullName "OpenClaw Service" -password "temp-change-me" -admin
Switch to the service user for setup
Switch to the service user for setup
su - openclaw
su - openclaw
Clone and configure OpenClaw in the user's home
Clone and configure OpenClaw in the user's home
cd ~
git clone https://github.com/openclaw/openclaw.git
cd openclaw
cp .env.example .env
cd ~
git clone https://github.com/openclaw/openclaw.git
cd openclaw
cp .env.example .env
Edit .env with production values
Edit .env with production values
undefinedundefinedSecrets Management
密钥管理
bash
undefinedbash
undefinedStore API keys in macOS Keychain instead of plaintext .env
Store API keys in macOS Keychain instead of plaintext .env
security add-generic-password -a openclaw -s "OPENAI_API_KEY" -w "sk-your-key-here"
security add-generic-password -a openclaw -s "ANTHROPIC_API_KEY" -w "sk-ant-your-key-here"
security add-generic-password -a openclaw -s "OPENAI_API_KEY" -w "sk-your-key-here"
security add-generic-password -a openclaw -s "ANTHROPIC_API_KEY" -w "sk-ant-your-key-here"
Retrieve a secret from Keychain in scripts
Retrieve a secret from Keychain in scripts
OPENAI_API_KEY=$(security find-generic-password -a openclaw -s "OPENAI_API_KEY" -w)
export OPENAI_API_KEY
OPENAI_API_KEY=$(security find-generic-password -a openclaw -s "OPENAI_API_KEY" -w)
export OPENAI_API_KEY
Helper script to load secrets from Keychain
Helper script to load secrets from Keychain
cat > /Users/openclaw/openclaw/load-secrets.sh << 'SCRIPT'
#!/usr/bin/env bash
export OPENAI_API_KEY=$(security find-generic-password -a openclaw -s "OPENAI_API_KEY" -w 2>/dev/null)
export ANTHROPIC_API_KEY=$(security find-generic-password -a openclaw -s "ANTHROPIC_API_KEY" -w 2>/dev/null)
SCRIPT
chmod 700 /Users/openclaw/openclaw/load-secrets.sh
undefinedcat > /Users/openclaw/openclaw/load-secrets.sh << 'SCRIPT'
#!/usr/bin/env bash
export OPENAI_API_KEY=$(security find-generic-password -a openclaw -s "OPENAI_API_KEY" -w 2>/dev/null)
export ANTHROPIC_API_KEY=$(security find-generic-password -a openclaw -s "ANTHROPIC_API_KEY" -w 2>/dev/null)
SCRIPT
chmod 700 /Users/openclaw/openclaw/load-secrets.sh
undefinedlaunchd Service Configuration
launchd 服务配置
xml
<!-- /Library/LaunchDaemons/com.openclaw.service.plist -->
<?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>Label</key>
<string>com.openclaw.service</string>
<key>UserName</key>
<string>openclaw</string>
<key>WorkingDirectory</key>
<string>/Users/openclaw/openclaw</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>source ./load-secrets.sh && /usr/local/bin/node ./dist/server.js</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>NODE_ENV</key>
<string>production</string>
<key>PORT</key>
<string>3000</string>
<key>HOST</key>
<string>0.0.0.0</string>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>ThrottleInterval</key>
<integer>10</integer>
<key>StandardOutPath</key>
<string>/var/log/openclaw/stdout.log</string>
<key>StandardErrorPath</key>
<string>/var/log/openclaw/stderr.log</string>
<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>65536</integer>
</dict>
</dict>
</plist>bash
undefinedxml
<!-- /Library/LaunchDaemons/com.openclaw.service.plist -->
<?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>Label</key>
<string>com.openclaw.service</string>
<key>UserName</key>
<string>openclaw</string>
<key>WorkingDirectory</key>
<string>/Users/openclaw/openclaw</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>source ./load-secrets.sh && /usr/local/bin/node ./dist/server.js</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>NODE_ENV</key>
<string>production</string>
<key>PORT</key>
<string>3000</string>
<key>HOST</key>
<string>0.0.0.0</string>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>ThrottleInterval</key>
<integer>10</integer>
<key>StandardOutPath</key>
<string>/var/log/openclaw/stdout.log</string>
<key>StandardErrorPath</key>
<string>/var/log/openclaw/stderr.log</string>
<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>65536</integer>
</dict>
</dict>
</plist>bash
undefinedCreate log directory
Create log directory
sudo mkdir -p /var/log/openclaw
sudo chown openclaw:staff /var/log/openclaw
sudo mkdir -p /var/log/openclaw
sudo chown openclaw:staff /var/log/openclaw
Load the service
Load the service
sudo launchctl load -w /Library/LaunchDaemons/com.openclaw.service.plist
sudo launchctl load -w /Library/LaunchDaemons/com.openclaw.service.plist
Verify it is running
Verify it is running
sudo launchctl list | grep openclaw
curl -f http://localhost:3000/api/health
sudo launchctl list | grep openclaw
curl -f http://localhost:3000/api/health
Stop/start/restart the service
Stop/start/restart the service
sudo launchctl stop com.openclaw.service
sudo launchctl start com.openclaw.service
sudo launchctl stop com.openclaw.service
sudo launchctl start com.openclaw.service
Unload the service (disable)
Unload the service (disable)
sudo launchctl unload /Library/LaunchDaemons/com.openclaw.service.plist
sudo launchctl unload /Library/LaunchDaemons/com.openclaw.service.plist
View logs
View logs
tail -f /var/log/openclaw/stdout.log
tail -f /var/log/openclaw/stderr.log
undefinedtail -f /var/log/openclaw/stdout.log
tail -f /var/log/openclaw/stderr.log
undefinedDocker Compose via launchd
通过launchd运行Docker Compose
xml
<!-- /Library/LaunchDaemons/com.openclaw.docker.plist -->
<?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>Label</key>
<string>com.openclaw.docker</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/docker</string>
<string>compose</string>
<string>-f</string>
<string>/Users/openclaw/openclaw/docker-compose.yml</string>
<string>up</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/var/log/openclaw/docker-stdout.log</string>
<key>StandardErrorPath</key>
<string>/var/log/openclaw/docker-stderr.log</string>
</dict>
</plist>xml
<!-- /Library/LaunchDaemons/com.openclaw.docker.plist -->
<?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>Label</key>
<string>com.openclaw.docker</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/docker</string>
<string>compose</string>
<string>-f</string>
<string>/Users/openclaw/openclaw/docker-compose.yml</string>
<string>up</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/var/log/openclaw/docker-stdout.log</string>
<key>StandardErrorPath</key>
<string>/var/log/openclaw/docker-stderr.log</string>
</dict>
</plist>Networking
网络设置
Tailscale for Secure Remote Access
使用Tailscale实现安全远程访问
bash
undefinedbash
undefinedInstall Tailscale on the Mac mini
Install Tailscale on the Mac mini
brew install --cask tailscale
brew install --cask tailscale
Authenticate and connect
Authenticate and connect
open /Applications/Tailscale.app
open /Applications/Tailscale.app
Or via CLI:
Or via CLI:
tailscale up --authkey tskey-auth-your-key-here
tailscale up --authkey tskey-auth-your-key-here
Verify Tailscale IP
Verify Tailscale IP
tailscale ip -4
tailscale ip -4
e.g., 100.64.x.x
e.g., 100.64.x.x
Access OpenClaw from any Tailscale device
Access OpenClaw from any Tailscale device
Enable MagicDNS for friendly names
Enable MagicDNS for friendly names
Access via: http://openclaw-mini:3000
Access via: http://openclaw-mini:3000
undefinedundefinedNginx Reverse Proxy (Optional)
Nginx反向代理(可选)
bash
undefinedbash
undefinedInstall nginx via Homebrew
Install nginx via Homebrew
brew install nginx
brew install nginx
Configure reverse proxy
Configure reverse proxy
cat > /opt/homebrew/etc/nginx/servers/openclaw.conf << 'NGINX'
server {
listen 80;
server_name openclaw-mini openclaw-mini.local;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
# Rate limiting for API endpoints
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}}
NGINX
cat > /opt/homebrew/etc/nginx/servers/openclaw.conf << 'NGINX'
server {
listen 80;
server_name openclaw-mini openclaw-mini.local;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
# Rate limiting for API endpoints
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}}
NGINX
Test and reload nginx
Test and reload nginx
nginx -t
brew services restart nginx
undefinednginx -t
brew services restart nginx
undefinedmacOS Firewall
macOS防火墙
bash
undefinedbash
undefinedEnable the application firewall
Enable the application firewall
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on
Allow specific apps
Allow specific apps
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add /usr/local/bin/node
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add /opt/homebrew/bin/nginx
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add /usr/local/bin/node
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add /opt/homebrew/bin/nginx
Block all incoming except allowed
Block all incoming except allowed
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setblockall on
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setblockall on
Verify
Verify
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
undefinedsudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
undefinedMonitoring
监控
Health Check Script
健康检查脚本
bash
#!/usr/bin/env bashbash
#!/usr/bin/env bash/Users/openclaw/openclaw/healthcheck.sh
/Users/openclaw/openclaw/healthcheck.sh
set -euo pipefail
ENDPOINT="http://localhost:3000/api/health"
LOGFILE="/var/log/openclaw/healthcheck.log"
ALERT_EMAIL="admin@example.com"
MAX_FAILURES=3
FAILURE_COUNT_FILE="/tmp/openclaw-failures"
timestamp() { date '+%Y-%m-%d %H:%M:%S'; }
set -euo pipefail
ENDPOINT="http://localhost:3000/api/health"
LOGFILE="/var/log/openclaw/healthcheck.log"
ALERT_EMAIL="admin@example.com"
MAX_FAILURES=3
FAILURE_COUNT_FILE="/tmp/openclaw-failures"
timestamp() { date '+%Y-%m-%d %H:%M:%S'; }
Initialize failure counter
Initialize failure counter
if [ ! -f "$FAILURE_COUNT_FILE" ]; then
echo 0 > "$FAILURE_COUNT_FILE"
fi
if curl -sf --max-time 10 "$ENDPOINT" > /dev/null 2>&1; then
echo "$(timestamp) OK" >> "$LOGFILE"
echo 0 > "$FAILURE_COUNT_FILE"
else
FAILURES=$(cat "$FAILURE_COUNT_FILE")
FAILURES=$((FAILURES + 1))
echo "$FAILURES" > "$FAILURE_COUNT_FILE"
echo "$(timestamp) FAIL (count: $FAILURES)" >> "$LOGFILE"
if [ "$FAILURES" -ge "$MAX_FAILURES" ]; then
echo "$(timestamp) ALERT: OpenClaw down for $FAILURES checks" >> "$LOGFILE"
# Attempt restart
sudo launchctl stop com.openclaw.service
sleep 2
sudo launchctl start com.openclaw.service
echo "$(timestamp) Service restarted" >> "$LOGFILE"
echo 0 > "$FAILURE_COUNT_FILE"
fi
fi
```bashif [ ! -f "$FAILURE_COUNT_FILE" ]; then
echo 0 > "$FAILURE_COUNT_FILE"
fi
if curl -sf --max-time 10 "$ENDPOINT" > /dev/null 2>&1; then
echo "$(timestamp) OK" >> "$LOGFILE"
echo 0 > "$FAILURE_COUNT_FILE"
else
FAILURES=$(cat "$FAILURE_COUNT_FILE")
FAILURES=$((FAILURES + 1))
echo "$FAILURES" > "$FAILURE_COUNT_FILE"
echo "$(timestamp) FAIL (count: $FAILURES)" >> "$LOGFILE"
if [ "$FAILURES" -ge "$MAX_FAILURES" ]; then
echo "$(timestamp) ALERT: OpenClaw down for $FAILURES checks" >> "$LOGFILE"
# Attempt restart
sudo launchctl stop com.openclaw.service
sleep 2
sudo launchctl start com.openclaw.service
echo "$(timestamp) Service restarted" >> "$LOGFILE"
echo 0 > "$FAILURE_COUNT_FILE"
fi
fi
```bashSchedule health checks every 5 minutes via cron
Schedule health checks every 5 minutes via cron
crontab -e
crontab -e
Add:
Add:
*/5 * * * * /Users/openclaw/openclaw/healthcheck.sh
*/5 * * * * /Users/openclaw/openclaw/healthcheck.sh
undefinedundefinedResource Monitoring
资源监控
bash
undefinedbash
undefinedMonitor CPU and memory usage of OpenClaw
Monitor CPU and memory usage of OpenClaw
ps aux | grep -E 'node|docker' | grep -v grep
ps aux | grep -E 'node|docker' | grep -v grep
Continuous monitoring with top (non-interactive)
Continuous monitoring with top (non-interactive)
top -l 1 -s 0 | grep -E 'node|docker'
top -l 1 -s 0 | grep -E 'node|docker'
Disk usage check
Disk usage check
df -h /Users/openclaw
du -sh /Users/openclaw/openclaw/data/
df -h /Users/openclaw
du -sh /Users/openclaw/openclaw/data/
Docker resource usage
Docker resource usage
docker stats --no-stream openclaw openclaw-redis openclaw-ollama
docker stats --no-stream openclaw openclaw-redis openclaw-ollama
macOS Activity Monitor from CLI
macOS Activity Monitor from CLI
sudo powermetrics --samplers cpu_power,gpu_power -n 1
undefinedsudo powermetrics --samplers cpu_power,gpu_power -n 1
undefinedLog Rotation
日志轮转
bash
undefinedbash
undefined/etc/newsyslog.d/openclaw.conf
/etc/newsyslog.d/openclaw.conf
logfilename [owner:group] mode count size when flags [/pid_file] [sig_num]
logfilename [owner:group] mode count size when flags [/pid_file] [sig_num]
/var/log/openclaw/stdout.log openclaw:staff 644 10 5120 * JN
/var/log/openclaw/stderr.log openclaw:staff 644 10 5120 * JN
/var/log/openclaw/healthcheck.log openclaw:staff 644 10 1024 * JN
```bash/var/log/openclaw/stdout.log openclaw:staff 644 10 5120 * JN
/var/log/openclaw/stderr.log openclaw:staff 644 10 5120 * JN
/var/log/openclaw/healthcheck.log openclaw:staff 644 10 1024 * JN
```bashForce log rotation
Force log rotation
sudo newsyslog -F
sudo newsyslog -F
Or use a simple cron-based rotation
Or use a simple cron-based rotation
cat > /Users/openclaw/rotate-logs.sh << 'ROTATE'
#!/usr/bin/env bash
LOGDIR="/var/log/openclaw"
for log in "$LOGDIR"/.log; do
if [ -f "$log" ] && [ "$(stat -f%z "$log")" -gt 52428800 ]; then
mv "$log" "${log}.$(date +%Y%m%d%H%M%S)"
gzip "${log}."
touch "$log"
fi
done
cat > /Users/openclaw/rotate-logs.sh << 'ROTATE'
#!/usr/bin/env bash
LOGDIR="/var/log/openclaw"
for log in "$LOGDIR"/.log; do
if [ -f "$log" ] && [ "$(stat -f%z "$log")" -gt 52428800 ]; then
mv "$log" "${log}.$(date +%Y%m%d%H%M%S)"
gzip "${log}."
touch "$log"
fi
done
Keep only last 10 rotated logs
Keep only last 10 rotated logs
ls -t "$LOGDIR"/*.gz 2>/dev/null | tail -n +11 | xargs rm -f
ROTATE
chmod +x /Users/openclaw/rotate-logs.sh
undefinedls -t "$LOGDIR"/*.gz 2>/dev/null | tail -n +11 | xargs rm -f
ROTATE
chmod +x /Users/openclaw/rotate-logs.sh
undefinedValidation Checklist
验证清单
- App starts after reboot without manual intervention ()
launchctl list | grep openclaw - Health check succeeds from local network ()
curl -f http://<ip>:3000/api/health - Health check succeeds via Tailscale ()
curl -f http://100.64.x.x:3000/api/health - Secrets are not committed and not world-readable (, check
ls -la .env).gitignore - Access to admin interfaces is restricted to trusted users/devices
- Docker volumes persist across container restarts ()
docker compose down && docker compose up -d - Log rotation is active and disk usage stays bounded
- Automatic restart works after crash (kill the process and verify relaunch)
- 重启后应用无需手动干预即可启动()
launchctl list | grep openclaw - 本地网络内健康检查成功()
curl -f http://<ip>:3000/api/health - 通过Tailscale健康检查成功()
curl -f http://100.64.x.x:3000/api/health - 密钥未提交到版本控制且不可被全局读取(,检查
ls -la .env).gitignore - 管理界面仅对可信用户/设备开放
- Docker卷在容器重启后可保留数据()
docker compose down && docker compose up -d - 日志轮转已启用且磁盘使用保持在可控范围
- 崩溃后自动重启功能正常(杀死进程并验证是否重新启动)
Troubleshooting
故障排查
| Symptom | Diagnostic | Fix |
|---|---|---|
| Slow responses | | Verify RAM/CPU pressure; use a smaller model or remote API |
| Boot failures | | Inspect |
| Auth errors | Check | Re-check provider keys, scopes, and endpoint URLs |
| Random crashes | | Pin dependency versions, check for OOM in |
| Port 3000 in use | | Kill conflicting process or change PORT in |
| Docker won't start | | Ensure Docker Desktop/OrbStack is running |
| Ollama model slow | | Allocate more RAM to Docker, use quantized model |
| Tailscale unreachable | | Re-authenticate with |
| Disk full | | Prune Docker images ( |
| 症状 | 诊断方法 | 修复方案 |
|---|---|---|
| 响应缓慢 | | 验证RAM/CPU负载;使用更小的模型或远程API |
| 启动失败 | | 检查 |
| 认证错误 | 检查 | 重新检查提供商密钥、权限范围和端点URL |
| 随机崩溃 | | 固定依赖版本,在 |
| 端口3000被占用 | | 杀死冲突进程或修改 |
| Docker无法启动 | | 确保Docker Desktop/OrbStack正在运行 |
| Ollama模型运行缓慢 | | 为Docker分配更多内存,使用量化模型 |
| Tailscale无法访问 | | 使用 |
| 磁盘已满 | | 清理Docker镜像( |
Related Skills
相关指南
- ollama-stack - Local model serving patterns
- mac-mini-llm-lab - Mac mini reliability and security baseline
- startup-it-troubleshooting - Small-team operational triage
- ollama-stack - 本地模型服务模式
- mac-mini-llm-lab - Mac mini可靠性与安全基线配置
- startup-it-troubleshooting - 小团队运维故障排查