openclaw-local-mac-mini

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

OpenClaw 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
undefined
bash
undefined

Clone the repository

Clone the repository

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
undefined
DATABASE_URL=sqlite:./data/openclaw.db ENV
undefined

Install Dependencies and Run

安装依赖并运行

bash
undefined
bash
undefined

Install 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

Expected: {"status":"ok","version":"..."}

Expected: {"status":"ok","version":"..."}

undefined
undefined

Validate the Setup

验证搭建结果

bash
undefined
bash
undefined

Check the API health endpoint

Check the API health endpoint

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
undefined
npm test
undefined

Docker 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
undefined
bash
undefined

Build 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
undefined
docker compose down -v
undefined

Mac mini Production Setup

Mac mini 生产环境配置

macOS Hardening and Baseline

macOS 加固与基础配置

bash
undefined
bash
undefined

Enable 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
undefined
pmset -g
undefined

Dedicated User Account

创建专用用户账户

bash
undefined
bash
undefined

Create 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

undefined
undefined

Secrets Management

密钥管理

bash
undefined
bash
undefined

Store 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
undefined
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
undefined

launchd 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
undefined
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
undefined

Create 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
undefined
tail -f /var/log/openclaw/stdout.log tail -f /var/log/openclaw/stderr.log
undefined

Docker 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
undefined
bash
undefined

Install 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

undefined
undefined

Nginx Reverse Proxy (Optional)

Nginx反向代理(可选)

bash
undefined
bash
undefined

Install 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
undefined
nginx -t brew services restart nginx
undefined

macOS Firewall

macOS防火墙

bash
undefined
bash
undefined

Enable 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
undefined
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
undefined

Monitoring

监控

Health Check Script

健康检查脚本

bash
#!/usr/bin/env bash
bash
#!/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

```bash
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

```bash

Schedule 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

undefined
undefined

Resource Monitoring

资源监控

bash
undefined
bash
undefined

Monitor 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
undefined
sudo powermetrics --samplers cpu_power,gpu_power -n 1
undefined

Log Rotation

日志轮转

bash
undefined
bash
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

```bash

Force 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
undefined
ls -t "$LOGDIR"/*.gz 2>/dev/null | tail -n +11 | xargs rm -f ROTATE chmod +x /Users/openclaw/rotate-logs.sh
undefined

Validation 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 (
    ls -la .env
    , check
    .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

故障排查

SymptomDiagnosticFix
Slow responses
top -l 1
, check model backend
Verify RAM/CPU pressure; use a smaller model or remote API
Boot failures
sudo launchctl list
, check logs
Inspect
/var/log/openclaw/stderr.log
, fix working directory
Auth errorsCheck
.env
or Keychain secrets
Re-check provider keys, scopes, and endpoint URLs
Random crashes
log show --predicate 'process == "node"'
Pin dependency versions, check for OOM in
dmesg
Port 3000 in use
lsof -i :3000
Kill conflicting process or change PORT in
.env
Docker won't start
docker info
,
docker compose logs
Ensure Docker Desktop/OrbStack is running
Ollama model slow
docker stats openclaw-ollama
Allocate more RAM to Docker, use quantized model
Tailscale unreachable
tailscale status
,
ping 100.64.x.x
Re-authenticate with
tailscale up
, check firewall
Disk full
df -h
,
du -sh ~/openclaw/data/
Prune Docker images (
docker system prune
), rotate logs
症状诊断方法修复方案
响应缓慢
top -l 1
,检查模型后端
验证RAM/CPU负载;使用更小的模型或远程API
启动失败
sudo launchctl list
,查看日志
检查
/var/log/openclaw/stderr.log
,修复工作目录配置
认证错误检查
.env
或Keychain中的密钥
重新检查提供商密钥、权限范围和端点URL
随机崩溃
log show --predicate 'process == "node"'
固定依赖版本,在
dmesg
中检查是否存在内存不足(OOM)情况
端口3000被占用
lsof -i :3000
杀死冲突进程或修改
.env
中的PORT配置
Docker无法启动
docker info
docker compose logs
确保Docker Desktop/OrbStack正在运行
Ollama模型运行缓慢
docker stats openclaw-ollama
为Docker分配更多内存,使用量化模型
Tailscale无法访问
tailscale status
ping 100.64.x.x
使用
tailscale up
重新认证,检查防火墙配置
磁盘已满
df -h
du -sh ~/openclaw/data/
清理Docker镜像(
docker system prune
),执行日志轮转

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 - 小团队运维故障排查