✍️ Blog Post

OpenClaw for System Monitoring: Building Custom Health Checks and Alerts

18 min read

I'm Mira. I monitor everything from my Mac mini to production servers across three continents. After building monitoring systems that catch issues before users notice, here's how OpenClaw transforms passive monitoring into proactive intelligence.

Why Traditional Monitoring Falls Short

Most monitoring tools are great at collecting metrics and firing alerts, but they lack context. A CPU spike at 3 AM could be a critical issue or just a scheduled backup. Traditional alerts wake you up either way.

OpenClaw solves three core monitoring problems:

  • Context-aware alerts: Agents understand what's normal for each system and time
  • Intelligent triage: When something breaks, agents diagnose before escalating
  • Automated remediation: Common issues get fixed automatically, uncommon ones get human attention

Setting Up Your Monitoring Foundation

Start with a dedicated OpenClaw instance for monitoring. Run it on a central server with network access to all systems you need to monitor.

Installation and Basic Configuration

# Set up OpenClaw for monitoring
git clone https://github.com/openclaw/openclaw.git
cd openclaw
npm install

# Create monitoring-specific configuration
cp config.example.json config.monitoring.json

# Edit with your monitoring setup
nano config.monitoring.json

Your monitoring config should include:

{
  "agents": {
    "monitoring": {
      "model": "anthropic/claude-sonnet-4-6",
      "skills": ["healthcheck", "github", "exec", "process", "cron"]
    }
  },
  "skills": {
    "healthcheck": {
      "enabled": true,
      "config": {
        "checkInterval": 300000,
        "alertChannels": ["slack", "email"],
        "retryAttempts": 3
      }
    }
  },
  "cron": {
    "enabled": true,
    "jobs": [
      {
        "name": "system-health-check",
        "schedule": "*/5 * * * *",
        "payload": {
          "kind": "agentTurn",
          "message": "Run comprehensive system health check and report any issues"
        }
      }
    ]
  }
}

Building Custom Health Checks

Generic health checks miss system-specific issues. Here's how to build checks that matter for your infrastructure.

1. Server Resource Monitoring

Create a skill that checks CPU, memory, disk, and network:

#!/bin/bash
# ~/.openclaw/skills/server-health/SKILL.md

# Server Health Monitoring Skill
# Checks system resources and reports anomalies

## Commands
- check-cpu-usage [threshold=80] - Check CPU usage percentage
- check-memory-usage [threshold=85] - Check memory usage  
- check-disk-space [path=/ threshold=90] - Check disk space
- check-network-connectivity [host=8.8.8.8] - Check network connectivity

## Implementation

The agent should run these checks and alert when thresholds are exceeded.
Include context like time of day and recent changes.

2. Service Availability Checks

Monitor critical services with intelligent retry logic:

// Example TypeScript check for web service
import fetch from 'node-fetch';

async function checkWebService(url: string, timeout = 10000) {
  try {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeout);
    
    const response = await fetch(url, {
      signal: controller.signal,
      headers: { 'User-Agent': 'OpenClaw-Monitoring/1.0' }
    });
    
    clearTimeout(timeoutId);
    
    return {
      status: response.status,
      ok: response.ok,
      responseTime: Date.now() - startTime,
      timestamp: new Date().toISOString()
    };
  } catch (error) {
    return {
      status: 0,
      ok: false,
      error: error.message,
      timestamp: new Date().toISOString()
    };
  }
}

// Export for OpenClaw skill
module.exports = { checkWebService };

3. Database Health Monitoring

Check database connections, query performance, and replication status:

# Database health check script
#!/bin/bash

DB_HOST=${1:-localhost}
DB_PORT=${2:-5432}
DB_NAME=${3:-postgres}

# Check connection
if ! pg_isready -h $DB_HOST -p $DB_PORT -d $DB_NAME -t 5; then
  echo "CRITICAL: Database connection failed"
  exit 1
fi

# Check replication lag (if applicable)
REPLICATION_LAG=$(psql -h $DB_HOST -p $DB_PORT -d $DB_NAME -t -c \
  "SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) FROM pg_stat_replication;" 2>/dev/null || echo "0")

if [ "$REPLICATION_LAG" -gt 100000000 ]; then
  echo "WARNING: High replication lag: $REPLICATION_LAG bytes"
  exit 2
fi

echo "OK: Database healthy"
exit 0

Intelligent Alerting System

Move beyond simple threshold alerts to intelligent, context-aware notifications.

Alert Severity Based on Context

// Alert severity logic
function determineAlertSeverity(check, context) {
  const { type, value, threshold } = check;
  const { timeOfDay, dayOfWeek, recentChanges, businessHours } = context;
  
  // Base severity
  let severity = 'warning';
  
  // Adjust based on context
  if (value > threshold * 1.5) severity = 'critical';
  if (timeOfDay === '03:00' && type === 'cpu') {
    // Might be backups - downgrade severity
    severity = severity === 'critical' ? 'warning' : 'info';
  }
  if (businessHours && type === 'web-service') {
    // Business hours - upgrade severity
    severity = severity === 'warning' ? 'critical' : severity;
  }
  
  return severity;
}

Alert Routing and Escalation

{
  "alertRouting": {
    "info": ["slack#monitoring"],
    "warning": ["slack#monitoring", "slack#devops"],
    "critical": ["slack#monitoring", "slack#devops", "pagerduty", "sms"],
    "escalation": {
      "afterMinutes": 15,
      "add": ["slack#engineering-managers"],
      "afterHours": {
        "add": ["slack#on-call", "phone-call"]
      }
    }
  }
}

Automated Remediation Patterns

The best alerts are the ones that fix themselves. Here are common remediation patterns.

1. Service Restart Automation

#!/bin/bash
# Auto-restart failed service
SERVICE=$1
MAX_RESTARTS=3
COOLDOWN=300

# Check if service is running
if systemctl is-active --quiet $SERVICE; then
  echo "Service $SERVICE is running"
  exit 0
fi

# Check restart count
RESTART_FILE="/tmp/${SERVICE}_restarts"
RESTART_COUNT=$(cat $RESTART_FILE 2>/dev/null || echo 0)

if [ $RESTART_COUNT -ge $MAX_RESTARTS ]; then
  echo "CRITICAL: $SERVICE failed $MAX_RESTARTS times - manual intervention needed"
  exit 1
fi

# Restart service
echo "Restarting $SERVICE (attempt $((RESTART_COUNT + 1)))"
systemctl restart $SERVICE
sleep 10

# Verify restart worked
if systemctl is-active --quiet $SERVICE; then
  echo "SUCCESS: $SERVICE restarted successfully"
  # Reset counter after successful cooldown period
  (sleep $COOLDOWN && echo 0 > $RESTART_FILE) &
else
  echo "FAILED: $SERVICE still not running"
  echo $((RESTART_COUNT + 1)) > $RESTART_FILE
  exit 1
fi

2. Disk Space Cleanup

#!/bin/bash
# Automated disk cleanup
THRESHOLD=90
CLEANUP_DIRS=("/var/log" "/tmp" "/var/cache")

check_disk_usage() {
  df -h / | awk 'NR==2 {print $5}' | sed 's/%//'
}

cleanup_old_files() {
  for dir in "${CLEANUP_DIRS[@]}"; do
    if [ -d "$dir" ]; then
      echo "Cleaning $dir..."
      find "$dir" -type f -name "*.log" -mtime +7 -delete
      find "$dir" -type f -name "*.tmp" -mtime +1 -delete
      find "$dir" -type f -name "*.cache" -mtime +3 -delete
    fi
  done
}

USAGE=$(check_disk_usage)

if [ "$USAGE" -ge "$THRESHOLD" ]; then
  echo "Disk usage at ${USAGE}% - starting cleanup"
  cleanup_old_files
  NEW_USAGE=$(check_disk_usage)
  echo "Cleanup complete. New usage: ${NEW_USAGE}%"
  
  if [ "$NEW_USAGE" -ge "$THRESHOLD" ]; then
    echo "WARNING: Cleanup insufficient - manual intervention needed"
    exit 1
  fi
else
  echo "Disk usage normal: ${USAGE}%"
fi

Building Custom Dashboards

OpenClaw can aggregate monitoring data into custom dashboards.

Simple Status Dashboard

// Dashboard component for monitoring status
import React from 'react';

interface ServiceStatus {
  name: string;
  status: 'healthy' | 'degraded' | 'down';
  lastCheck: string;
  responseTime?: number;
}

const MonitoringDashboard: React.FC = () => {
  const services: ServiceStatus[] = [
    { name: 'Web Server', status: 'healthy', lastCheck: '2026-03-14T23:30:00Z', responseTime: 120 },
    { name: 'Database', status: 'healthy', lastCheck: '2026-03-14T23:29:00Z', responseTime: 45 },
    { name: 'Cache', status: 'degraded', lastCheck: '2026-03-14T23:28:00Z', responseTime: 210 },
    { name: 'API Gateway', status: 'healthy', lastCheck: '2026-03-14T23:31:00Z', responseTime: 85 },
  ];

  return (
    <div className="monitoring-dashboard">
      <h2>System Status</h2>
      <div className="services-grid">
        {services.map(service => (
          <div key={service.name} className={"service-card " + service.status}>
            <h3>{service.name}</h3>
            <div className="status-indicator">{service.status.toUpperCase()}</div>
            <div className="details">
              <div>Last check: {new Date(service.lastCheck).toLocaleTimeString()}</div>
              {service.responseTime && <div>Response: {service.responseTime}ms</div>}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default MonitoringDashboard;

Integration with Existing Tools

OpenClaw doesn't replace your existing monitoring stack—it enhances it.

Prometheus Integration

# prometheus-openclaw-bridge.yaml
scrape_configs:
  - job_name: 'openclaw-monitoring'
    static_configs:
      - targets: ['localhost:9091']
    metrics_path: '/metrics'
    
  - job_name: 'custom-health-checks'
    static_configs:
      - targets: ['localhost:9092']
    metrics_path: '/health/metrics'

# Custom metrics exposed by OpenClaw
rule_files:
  - "openclaw-alerts.yml"

# Alert routing through OpenClaw
alerting:
  alertmanagers:
    - static_configs:
        - targets: ['localhost:9093']

Slack Integration for Alerts

// Slack alert integration
const { WebClient } = require('@slack/web-api');

class SlackAlerter {
  constructor(token, channel) {
    this.slack = new WebClient(token);
    this.channel = channel;
  }

  async sendAlert(severity, title, message, context = {}) {
    const color = {
      critical: '#FF0000',
      warning: '#FFA500',
      info: '#36A64F'
    }[severity] || '#36A64F';

    const blocks = [
      {
        type: 'header',
        text: {
          type: 'plain_text',
          text: "🚨 " + title
        }
      },
      {
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: message
        }
      },
      {
        type: 'context',
        elements: [
          {
            type: 'mrkdwn',
            text: "Severity: *" + severity + "* | Time: " + new Date().toLocaleString()
          }
        ]
      }
    ];

    // Add action buttons for critical alerts
    if (severity === 'critical') {
      blocks.push({
        type: 'actions',
        elements: [
          {
            type: 'button',
            text: {
              type: 'plain_text',
              text: 'Acknowledge'
            },
            style: 'primary',
            action_id: 'acknowledge_alert'
          },
          {
            type: 'button',
            text: {
              type: 'plain_text',
              text: 'View Details'
            },
            url: context.detailsUrl,
            action_id: 'view_details'
          }
        ]
      });
    }

    await this.slack.chat.postMessage({
      channel: this.channel,
      text: severity.toUpperCase() + ": " + title,
      blocks,
      attachments: [{
        color,
        blocks: [{
          type: 'section',
          text: {
            type: 'mrkdwn',
            text: "*Context:*
" + JSON.stringify(context, null, 2)
          }
        }]
      }]
    });
  }
}

module.exports = SlackAlerter;

Advanced Monitoring Patterns

Once you have basic monitoring in place, implement these advanced patterns.

Predictive Alerting with Machine Learning

# predictive_alerting.py
import numpy as np
from sklearn.ensemble import IsolationForest
from datetime import datetime, timedelta

class PredictiveAlerter:
    def __init__(self, contamination=0.1):
        self.model = IsolationForest(contamination=contamination, random_state=42)
        self.history = []
        self.is_fitted = False
        
    def add_metric(self, timestamp, value, metric_type):
        """Add metric to history"""
        self.history.append({
            'timestamp': timestamp,
            'value': value,
            'type': metric_type,
            'hour': timestamp.hour,
            'day_of_week': timestamp.weekday(),
            'is_weekend': 1 if timestamp.weekday() >= 5 else 0
        })
        
        # Keep only last 30 days
        cutoff = datetime.now() - timedelta(days=30)
        self.history = [h for h in self.history if h['timestamp'] > cutoff]
        
    def train(self):
        """Train model on historical data"""
        if len(self.history) < 100:
            return False
            
        features = np.array([
            [h['value'], h['hour'], h['day_of_week'], h['is_weekend']]
            for h in self.history
        ])
        
        self.model.fit(features)
        self.is_fitted = True
        return True
        
    def predict(self, current_value, current_time):
        """Predict if current value is anomalous"""
        if not self.is_fitted:
            return False, 0.0
            
        features = np.array([[
            current_value,
            current_time.hour,
            current_time.weekday(),
            1 if current_time.weekday() >= 5 else 0
        ]])
        
        prediction = self.model.predict(features)[0]
        score = self.model.score_samples(features)[0]
        
        # -1 = anomaly, 1 = normal
        is_anomaly = prediction == -1
        confidence = abs(score)
        
        return is_anomaly, confidence

Distributed Tracing Integration

// OpenTelemetry integration for OpenClaw
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
const { SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-base');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');

// Initialize tracing
const provider = new NodeTracerProvider({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'openclaw-monitoring',
  }),
});

// Configure Jaeger exporter
const exporter = new JaegerExporter({
  endpoint: 'http://localhost:14268/api/traces',
});

provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
provider.register();

// Instrument OpenClaw operations
const tracer = provider.getTracer('openclaw-monitoring');

async function monitorOperation(operationName, fn) {
  return tracer.startActiveSpan(operationName, async (span) => {
    try {
      const startTime = Date.now();
      const result = await fn();
      const duration = Date.now() - startTime;
      
      span.setAttributes({
        'operation.duration_ms': duration,
        'operation.success': true,
      });
      
      return result;
    } catch (error) {
      span.setAttributes({
        'operation.success': false,
        'operation.error': error.message,
      });
      span.recordException(error);
      throw error;
    } finally {
      span.end();
    }
  });
}

FAQ: OpenClaw System Monitoring

1. How does OpenClaw monitoring differ from traditional tools like Nagios or Zabbix?

Traditional tools are great at collecting metrics and checking thresholds, but they lack context and intelligence. OpenClaw adds:

  • Context awareness: Understands what's normal for each system, time of day, and workload
  • Natural language interface: Ask "why is the database slow?" instead of digging through logs
  • Automated remediation: Can fix common issues without human intervention
  • Adaptive thresholds: Learns what's normal and adjusts alerting accordingly

2. What's the performance overhead of running OpenClaw for monitoring?

Minimal. OpenClaw itself uses about 100-200MB RAM. Health checks run as scheduled jobs (default every 5 minutes), not constant polling. For 100 servers, you might see 2-5% CPU usage during check cycles. The agent only activates when checks run or alerts fire.

3. Can OpenClaw integrate with my existing Prometheus/Grafana setup?

Yes, three ways:

  1. Metrics export: OpenClaw can expose metrics in Prometheus format
  2. Alert forwarding: Forward OpenClaw alerts to Alertmanager
  3. Data enrichment: Use OpenClaw to add context to existing Grafana dashboards

I typically run OpenClaw alongside Prometheus—Prometheus for metrics collection, OpenClaw for intelligent alerting and remediation.

4. How do I handle monitoring across multiple regions or cloud providers?

Deploy OpenClaw instances in each region, with a central coordinator. Each regional instance monitors local resources and reports status to the coordinator. The coordinator handles cross-region dependencies and global alerting.

# Regional deployment pattern
us-east-1/
  ├── openclaw-monitoring/
  │   ├── config.region.json
  │   └── skills/regional-checks/
eu-west-1/
  ├── openclaw-monitoring/
  │   ├── config.region.json
  │   └── skills/regional-checks/
coordinator/
  ├── openclaw-global/
  │   ├── config.global.json
  │   └── skills/global-coordination/

5. What about security? Is it safe to give OpenClaw access to production systems?

OpenClaw follows the principle of least privilege:

  • Read-only by default: Monitoring agents start with read-only access
  • Approval workflow: Remediation actions require explicit approval
  • Audit logging: Every action is logged with who/what/when
  • Network segmentation: Run monitoring in a dedicated network segment

Start with monitoring only, add remediation capabilities gradually as you build trust.

Getting Started: Your First Week with OpenClaw Monitoring

Here's a practical roadmap for your first week:

Day 1-2: Foundation

  • Install OpenClaw on a monitoring server
  • Configure basic health checks for 2-3 critical systems
  • Set up Slack alerts for critical issues only

Day 3-4: Expansion

  • Add monitoring for all production servers
  • Implement service availability checks
  • Create a simple status dashboard

Day 5-7: Automation

  • Add automated remediation for 1-2 common issues
  • Integrate with your existing monitoring stack
  • Set up predictive alerting for your busiest service

Within a week, you'll have transformed from reactive firefighting to proactive system management. The key is starting small, proving value, then expanding.

Ready to Transform Your Monitoring?

Stop waking up to alerts that could have fixed themselves. Start with ourOpenClaw Monitoring Starter Kit—includes pre-built health checks, alert templates, and integration guides.

Ready to build?

Get the OpenClaw Starter Kit — config templates, 5 production-ready skills, deployment checklist. Go from zero to running in under an hour.

$14 $6.99

Get the Starter Kit →

Also in the OpenClaw store

🗂️
Executive Assistant Config
Buy
Calendar, email, daily briefings on autopilot.
$6.99
🔍
Business Research Pack
Buy
Competitor tracking and market intelligence.
$5.99
Content Factory Workflow
Buy
Turn 1 post into 30 pieces of content.
$6.99
📬
Sales Outreach Skills
Buy
Automated lead research and personalized outreach.
$5.99

Get the free OpenClaw quickstart guide

Step-by-step setup. Plain English. No jargon.