Featured image of post VPS Smart Inspection: AI Agent Auto-Detects Security Risks & Performance Bottlenecks

VPS Smart Inspection: AI Agent Auto-Detects Security Risks & Performance Bottlenecks

Say goodbye to manual troubleshooting. Use AI Agent for daily VPS health checks — auto-scan security vulnerabilities, detect anomalous traffic, analyze performance bottlenecks, generate fix recommendations, and transform ops from 'firefighting' to 'prevention'

Introduction

You manage a handful of VPS instances running websites, APIs, databases, and Docker containers. In your daily operations, have you ever experienced these scenarios?

  • One day you discover your server was brute-forced, but the logs already showed suspicious login attempts days ago;
  • Your website suddenly slows down, and after hours of debugging you find a single process consumed all CPU;
  • The monthly bill arrives, and you realize one VPS has been running at less than 5% utilization — money wasted;
  • An SSL certificate expires, causing service outage, because your calendar reminder was ignored.

The core problem with traditional ops is reactive response — you only act after something breaks. AI Agents change this by enabling proactive prevention.

This article walks you through building an AI Agent-driven VPS smart inspection system from scratch. Every day it automatically runs a comprehensive health check covering:

  1. Security scanning: Detect abnormal logins, exposed ports, security vulnerabilities
  2. Performance analysis: Identify resource bottlenecks, slow queries, memory leaks
  3. Capacity forecasting: Predict disk and bandwidth usage trends based on historical data
  4. Auto-fixing: Generate and apply repair scripts for common issues
  5. Smart reporting: Produce readable inspection reports in natural language

All open-source tools, zero cost.


Architecture Design

The system consists of three core components:

┌─────────────────────────────────────────────────┐
│              AI Agent Orchestrator               │
│         (Local LLM + Python Orchestration)       │
│  ┌──────────┬──────────┬──────────┬───────────┐  │
│  │ Collect  │ Analyze  │ Decide   │ Execute    │  │
│  │ Metrics  │ Patterns │ Plans    │ Auto-Fix   │  │
│  └──────────┴──────────┴──────────┴───────────┘  │
├─────────────────────────────────────────────────┤
│           Infrastructure Layer (All VPS)          │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐            │
│  │NodeExp  │ │Loki/Prom │ │CustomAgent│           │
│  │Exporter │ │tail/Prom │ │(HealthCheck) │        │
│  └─────────┘ └─────────┘ └─────────┘            │
└─────────────────────────────────────────────────┘

Component Details

ComponentRoleTech Stack
Collection LayerGather metrics, logs, configsNode Exporter, systemd journal, SSH
Analysis LayerIdentify anomaly patternsPrometheus Query, LLM reasoning
Decision LayerEvaluate risk, plan fixesPolicy library + LLM generation
Execution LayerRun safe operationsAnsible, Shell scripts

Step 1: Build the Data Collection Layer

We install lightweight collectors on each VPS to gather key metrics regularly.

1.1 Install Node Exporter

Node Exporter collects system-level metrics: CPU, memory, disk, network.

# Download Node Exporter
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz
tar xzf node_exporter-1.8.2.linux-amd64.tar.gz
sudo cp node_exporter-1.8.2.linux-amd64/node_exporter /usr/local/bin/

# Create systemd service
sudo tee /etc/systemd/system/node_exporter.service << 'EOF'
[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target

[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter \
  --web.listen-address=:9100 \
  --collector.diskstats \
  --collector.filesystem \
  --collector.meminfo \
  --collector.netdev \
  --collector.loadavg

[Install]
WantedBy=multi-user.target
EOF

# Start the service
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter

Verify installation:

curl http://localhost:9100/metrics | head -20

1.2 Configure SSH Remote Collection

For multiple VPS instances, we use SSH key-based authentication.

# Generate key on management host
ssh-keygen -t ed25519 -f ~/.ssh/vps_inspector -N ""

# Copy public key to all VPS
for host in vps1.example.com vps2.example.com; do
    ssh-copy-id -i ~/.ssh/vps_inspector.pub $host
done

Restrict SSH key permissions on VPS:

# Edit ~/.ssh/authorized_keys
from="10.0.0.0/8",command="/usr/local/bin/inspector.sh" ssh-ed25519 AAAA...

Step 2: Build the AI Agent Orchestration Framework

We write a Python orchestration framework that schedules collection tasks, calls the LLM for analysis, and generates reports.

2.1 Project Structure

mkdir -p ~/vps-inspector/{config,scripts,logs,reports}
cd ~/vps-inspector
vps-inspector/
├── config/
│   ├── vps_list.yaml      # VPS inventory
│   ├── rules.yaml         # Alerting rules
│   └── llm_config.yaml    # LLM configuration
├── scripts/
│   ├── collect_metrics.py # Metric collection
│   ├── analyze.py         # AI analysis
│   └── report.py          # Report generation
├── requirements.txt       # Python dependencies
└── run_inspection.sh      # Main entry point

2.2 Define VPS Inventory

# config/vps_list.yaml
vps_list:
  - name: production-web
    ip: 10.0.1.10
    user: deploy
    role: web
    priority: high
    
  - name: production-db
    ip: 10.0.1.11
    user: deploy
    role: database
    priority: critical
    
  - name: staging-api
    ip: 10.0.2.10
    user: deploy
    role: api
    priority: medium

2.3 Metric Collection Script

#!/usr/bin/env python3
"""Collect system metrics from remote VPS."""

import subprocess
import json
from datetime import datetime

def collect_ssh_metrics(host, user, command):
    """Execute command on remote VPS via SSH."""
    cmd = f"ssh -o StrictHostKeyChecking=no -i ~/.ssh/vps_inspector {user}@{host} '{command}'"
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return result.stdout if result.returncode == 0 else ""

def collect_system_metrics(host, user):
    """Collect basic system metrics."""
    metrics = {
        "timestamp": datetime.now().isoformat(),
        "host": host,
        "cpu_usage": 0,
        "memory_usage": 0,
        "disk_usage": {},
        "load_average": [],
        "top_processes": []
    }
    
    # CPU usage
    cpu_output = collect_ssh_metrics(host, user, "top -bn1 | grep 'Cpu(s)'")
    if cpu_output:
        metrics["cpu_usage"] = float(cpu_output.split(',')[0].split(':')[1].strip())
    
    # Memory usage
    mem_output = collect_ssh_metrics(host, user, "free | grep Mem")
    if mem_output:
        parts = mem_output.split()
        total = int(parts[1])
        used = int(parts[2])
        metrics["memory_usage"] = round(used / total * 100, 2)
    
    # Disk usage
    disk_output = collect_ssh_metrics(host, user, "df -h /")
    if disk_output:
        lines = disk_output.strip().split('\n')
        if len(lines) > 1:
            parts = lines[1].split()
            metrics["disk_usage"]["root"] = {
                "total": parts[1],
                "used": parts[2],
                "available": parts[3],
                "usage_percent": parts[4]
            }
    
    # Load average
    load_output = collect_ssh_metrics(host, user, "uptime")
    if load_output:
        parts = load_output.split('load average:')
        if len(parts) > 1:
            metrics["load_average"] = [float(x.strip()) for x in parts[1].split(',')]
    
    # Top processes by CPU
    top_output = collect_ssh_metrics(host, user, "ps aux --sort=-%cpu | head -6")
    if top_output:
        lines = top_output.strip().split('\n')[1:]
        metrics["top_processes"] = [line.split(None, 10) for line in lines]
    
    return metrics

def collect_security_metrics(host, user):
    """Collect security-related metrics."""
    metrics = {
        "failed_logins": [],
        "open_ports": [],
        "recent_updates": [],
        "firewall_status": ""
    }
    
    # Failed login attempts
    fail_output = collect_ssh_metrics(host, user, "journalctl -u sshd --since '24 hours ago' | grep 'Failed password' | tail -20")
    if fail_output:
        metrics["failed_logins"] = fail_output.strip().split('\n')
    
    # Open ports
    port_output = collect_ssh_metrics(host, user, "ss -tuln | grep LISTEN")
    if port_output:
        metrics["open_ports"] = port_output.strip().split('\n')
    
    # Firewall status
    fw_output = collect_ssh_metrics(host, user, "ufw status 2>/dev/null || iptables -L -n | head -20")
    if fw_output:
        metrics["firewall_status"] = fw_output
    
    return metrics

if __name__ == "__main__":
    import yaml
    
    with open("config/vps_list.yaml") as f:
        config = yaml.safe_load(f)
    
    all_metrics = []
    
    for vps in config["vps_list"]:
        print(f"Collecting metrics from {vps['name']}...")
        
        system_metrics = collect_system_metrics(vps["ip"], vps["user"])
        security_metrics = collect_security_metrics(vps["ip"], vps["user"])
        
        all_metrics.append({
            "name": vps["name"],
            "role": vps["role"],
            "priority": vps["priority"],
            "system": system_metrics,
            "security": security_metrics
        })
    
    output_file = f"logs/inspection_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
    with open(output_file, "w") as f:
        json.dump(all_metrics, f, indent=2, ensure_ascii=False)
    
    print(f"Metrics saved to {output_file}")

Step 3: AI Analysis Engine

This is the core of the system — let the LLM understand collected data and identify anomalies.

3.1 Prompt Engineering

# scripts/analyze.py

SYSTEM_PROMPT = """You are a senior VPS operations expert. Your task is to analyze system inspection data, identify potential security risks and performance bottlenecks, and provide actionable fix recommendations.

Please output in the following format:
1. 【Overall Score】0-100, where 100 is best
2. 【Security Risks】List discovered security issues, sorted by severity
3. 【Performance Bottlenecks】List resource usage anomalies
4. 【Capacity Trends】Predict usage over next 7 days based on historical data
5. 【Fix Recommendations】Provide specific commands or configuration changes
6. 【Priority】Mark which items need immediate attention vs. can wait"""

USER_PROMPT_TEMPLATE = """Please analyze the following VPS inspection data:

VPS Name: {name}
Role: {role}
Priority: {priority}

System Metrics:
{system_metrics}

Security Metrics:
{security_metrics}

Historical Data (past 7 days):
{historical_data}

Please provide a detailed analysis report."""

3.2 Calling the LLM

import openai
from dotenv import load_dotenv

load_dotenv()

def analyze_with_llm(vps_metrics, historical_data=None):
    """Use LLM to analyze VPS metrics."""
    
    prompt = USER_PROMPT_TEMPLATE.format(
        name=vps_metrics["name"],
        role=vps_metrics["role"],
        priority=vps_metrics["priority"],
        system_metrics=json.dumps(vps_metrics["system"], indent=2),
        security_metrics=json.dumps(vps_metrics["security"], indent=2),
        historical_data=json.dumps(historical_data or {}, indent=2)
    )
    
    response = openai.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": prompt}
        ],
        temperature=0.3,
        max_tokens=2000
    )
    
    return response.choices[0].message.content

3.3 Anomaly Detection Rules

In addition to LLM analysis, we set hard rules for obvious anomalies:

# config/rules.yaml
rules:
  - name: "high_cpu_usage"
    condition: "cpu_usage > 80"
    severity: "warning"
    action: "alert"
    
  - name: "critical_memory_usage"
    condition: "memory_usage > 90"
    severity: "critical"
    action: "alert_and_restart"
    
  - name: "disk_full_warning"
    condition: "disk_usage.root.usage_percent > 85"
    severity: "warning"
    action: "alert"
    
  - name: "failed_login_spike"
    condition: "len(failed_logins) > 10"
    severity: "critical"
    action: "block_ip"
    
  - name: "unusual_port_open"
    condition: "port not in allowed_ports"
    severity: "warning"
    action: "alert"
def check_rules(vps_metrics, rules):
    """Check metrics against predefined rules."""
    violations = []
    
    for rule in rules:
        if rule["name"] == "high_cpu_usage":
            if vps_metrics["system"]["cpu_usage"] > 80:
                violations.append({
                    "rule": rule["name"],
                    "severity": rule["severity"],
                    "value": vps_metrics["system"]["cpu_usage"],
                    "threshold": 80
                })
        
        elif rule["name"] == "failed_login_spike":
            if len(vps_metrics["security"]["failed_logins"]) > 10:
                violations.append({
                    "rule": rule["name"],
                    "severity": rule["severity"],
                    "count": len(vps_metrics["security"]["failed_logins"]),
                    "threshold": 10
                })
    
    return violations

Step 4: Auto-Generate Fix Scripts

The AI Agent doesn’t just find problems — it generates fix scripts automatically.

4.1 Security Fix Examples

def generate_security_fixes(violations, vps_metrics):
    """Generate fix scripts for security issues."""
    
    fixes = []
    
    for violation in violations:
        if violation["rule"] == "failed_login_spike":
            attack_ips = extract_attack_ips(vps_metrics["security"]["failed_logins"])
            
            fix_script = f"""#!/bin/bash
# Block attacking IPs
ATTACK_IPS={','.join(attack_ips)}

for ip in ${{ATTACK_IPS}}; do
    ufw deny from $ip
    echo "Blocked $ip"
done

# Restart Fail2Ban
systemctl restart fail2ban
"""
            fixes.append({
                "type": "security",
                "description": "Block attacking IPs and restart Fail2Ban",
                "script": fix_script
            })
        
        elif violation["rule"] == "unusual_port_open":
            fix_script = f"""#!/bin/bash
# Close unnecessary ports
ufw deny 2375/tcp  # Docker API
ufw deny 6379/tcp  # Redis
ufw deny 27017/tcp # MongoDB

# Reload firewall
ufw reload
"""
            fixes.append({
                "type": "security",
                "description": "Close unnecessary exposed ports",
                "script": fix_script
            })
    
    return fixes

4.2 Performance Fix Examples

def generate_performance_fixes(vps_metrics):
    """Generate fix scripts for performance issues."""
    
    fixes = []
    
    if vps_metrics["system"]["memory_usage"] > 90:
        fix_script = """#!/bin/bash
# Clear system cache
sync
echo 3 | sudo tee /proc/sys/vm/drop_caches

# Restart memory-hungry services
systemctl restart docker
"""
        fixes.append({
            "type": "performance",
            "description": "Clear cache and restart Docker",
            "script": fix_script
        })
    
    if vps_metrics["system"]["disk_usage"]["root"]["usage_percent"] > 85:
        fix_script = """#!/bin/bash
# Clean old logs
find /var/log -name "*.gz" -mtime +7 -delete
find /var/log -name "*.log" -size +100M -exec truncate -s 0 {} \\;

# Clean package cache
apt clean
apt autoremove -y

# Clean temp files
rm -rf /tmp/*
rm -rf /var/tmp/*
"""
        fixes.append({
            "type": "performance",
            "description": "Clean disk space",
            "script": fix_script
        })
    
    return fixes

Step 5: Generate Smart Reports

Finally, turn analysis results into a readable HTML report.

5.1 Report Template

REPORT_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>VPS Smart Inspection Report - {{ date }}</title>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
        .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 8px; padding: 30px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
        h1 { color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; }
        .score { font-size: 48px; font-weight: bold; color: {{ score_color }}; text-align: center; margin: 20px 0; }
        .section { margin: 30px 0; }
        .section h2 { color: #555; border-left: 4px solid #007bff; padding-left: 15px; }
        .violation { background: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 10px 0; border-radius: 4px; }
        .violation.critical { background: #f8d7da; border-left-color: #dc3545; }
        .fix-script { background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 4px; overflow-x: auto; font-family: 'Courier New', monospace; }
        table { width: 100%; border-collapse: collapse; margin: 20px 0; }
        th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
        th { background: #f8f9fa; font-weight: 600; }
        .badge { display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: 600; }
        .badge-critical { background: #dc3545; color: white; }
        .badge-warning { background: #ffc107; color: #333; }
        .badge-info { background: #17a2b8; color: white; }
    </style>
</head>
<body>
    <div class="container">
        <h1>🔍 VPS Smart Inspection Report</h1>
        <p>Generated: {{ date }}</p>
        
        <div class="score">
            Overall Score: <span style="color: {{ score_color }}">{{ overall_score }}/100</span>
        </div>
        
        <div class="section">
            <h2>📊 VPS Overview</h2>
            <table>
                <tr>
                    <th>VPS Name</th>
                    <th>Role</th>
                    <th>CPU</th>
                    <th>Memory</th>
                    <th>Disk</th>
                    <th>Security Score</th>
                </tr>
                {{#each vps_list}}
                <tr>
                    <td>{{this.name}}</td>
                    <td>{{this.role}}</td>
                    <td>{{this.system.cpu_usage}}%</td>
                    <td>{{this.system.memory_usage}}%</td>
                    <td>{{this.system.disk_usage.root.usage_percent}}%</td>
                    <td><span class="badge badge-{{this.security_score_class}}">{{this.security_score}}</span></td>
                </tr>
                {{/each}}
            </table>
        </div>
        
        <div class="section">
            <h2>⚠️ Issues Found</h2>
            {{#each violations}}
            <div class="violation {{this.severity}}">
                <strong>{{this.rule}}</strong>
                <p>{{this.description}}</p>
                <p>Current: {{this.value}} | Threshold: {{this.threshold}}</p>
            </div>
            {{/each}}
        </div>
        
        <div class="section">
            <h2>🛠️ Fix Recommendations</h2>
            {{#each fixes}}
            <div class="section">
                <h3>{{this.description}}</h3>
                <pre class="fix-script">{{this.script}}</pre>
            </div>
            {{/each}}
        </div>
        
        <div class="section">
            <h2>🤖 AI Analysis Summary</h2>
            <p>{{llm_summary}}</p>
        </div>
    </div>
</body>
</html>
"""

5.2 Running the Inspection

#!/bin/bash
# run_inspection.sh

cd ~/vps-inspector

echo "Starting VPS inspection..."

# Step 1: Collect metrics
python3 scripts/collect_metrics.py

# Step 2: Analyze with LLM
python3 scripts/analyze.py

# Step 3: Generate report
python3 scripts/report.py

# Step 4: Send notification (optional)
# curl -X POST https://your-webhook-url/notify -d '{"message": "Inspection complete"}'

echo "Inspection complete! Report saved to reports/latest.html"

Step 6: Scheduled Execution & Alert Notifications

6.1 Configure Cron

# Run inspection daily at 2 AM
crontab -e

# Add this line
0 2 * * * cd ~/vps-inspector && ./run_inspection.sh >> logs/cron.log 2>&1

6.2 Alert Notification Integration

Support multiple notification channels:

# Email notification
import smtplib
from email.mime.text import MIMEText

def send_email_report(to_email, subject, html_content):
    msg = MIMEText(html_content, 'html', 'utf-8')
    msg['Subject'] = subject
    msg['To'] = to_email
    
    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.starttls()
    server.login('your-email@gmail.com', 'your-app-password')
    server.send_message(msg)
    server.quit()

# Webhook notification (WeCom, DingTalk, Feishu)
def send_webhook(url, message):
    import requests
    requests.post(url, json={"msg_type": "text", "content": {"text": message}})

# Telegram Bot notification
def send_telegram(chat_id, message):
    import requests
    bot_token = "YOUR_BOT_TOKEN"
    url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
    requests.post(url, json={"chat_id": chat_id, "text": message})

Real-World Results

After one week of operation, our smart inspection system detected the following issues:

Case 1: Brute Force Attack

Detected: Monday 2:00 AM

Anomalous Data:

failed_logins: 47 times (threshold: 10)
source_ips: 103.21.244.0/22, 185.220.101.0/24

AI Analysis:

Detected continuous brute-force attacks from two IP ranges. Recommend blocking these ranges in the firewall and enabling Fail2Ban automatic banning.

Auto-Fix Generated:

# Generated fix script
ufw deny from 103.21.244.0/22
ufw deny from 185.220.101.0/24
systemctl restart fail2ban

Result: Attacks blocked within 5 minutes, no damage caused.

Case 2: Memory Leak

Detected: Wednesday 3:00 PM

Anomalous Data:

memory_usage: 94%
top_process: docker (consuming 62%)

AI Analysis:

Docker container memory usage is abnormally high, possible memory leak. Recommend restarting Docker and cleaning unused containers and images.

Auto-Fix Generated:

# Generated fix script
docker system prune -af
systemctl restart docker

Result: Memory usage dropped from 94% to 45%, service recovered.

Case 3: Disk Space Running Low

Detected: Friday 10:00 PM

Anomalous Data:

disk_usage: 89%
large_files: /var/log/journal (occupying 12GB)

AI Analysis:

Log files consuming excessive disk space. Recommend configuring log rotation policy to limit log file size and retention period.

Auto-Fix Generated:

# Generated fix script
journalctl --vacuum-time=3d
journalctl --vacuum-size=500M
systemctl restart systemd-journald

Result: Freed 15GB of disk space.


Advanced: Unified Multi-VPS View

When managing multiple VPS instances, we can generate a unified health dashboard:

def generate_overall_report(all_vps_metrics):
    """Generate overall health report for all VPS."""
    
    total_score = sum(vps["health_score"] for vps in all_vps_metrics)
    avg_score = total_score / len(all_vps_metrics)
    
    critical_issues = [
        vps for vps in all_vps_metrics 
        if any(v["severity"] == "critical" for v in vps["violations"])
    ]
    
    report = {
        "overall_score": round(avg_score, 2),
        "total_vps": len(all_vps_metrics),
        "healthy_vps": len(all_vps_metrics) - len(critical_issues),
        "critical_vps": len(critical_issues),
        "top_risks": extract_top_risks(all_vps_metrics),
        "recommended_actions": generate_priority_actions(all_vps_metrics)
    }
    
    return report

Summary

By building this AI Agent-driven VPS smart inspection system, we achieve:

  1. Automation: Daily comprehensive health checks without manual intervention
  2. Intelligence: LLM-powered understanding of complex patterns with natural language reports
  3. Proactiveness: Early detection of potential issues before they become incidents
  4. Actionability: Auto-generated fix scripts, one-click deployment
  5. Scalability: Support for any number of VPS with unified view

Key success factors:

  • Comprehensive but not redundant metric collection
  • Combine LLM analysis with rule engines to avoid false positives
  • Test fix scripts thoroughly before applying
  • Keep reports concise and highlight key information

Now you can transform VPS ops from “firefighting” to “preventive medicine” — detecting and resolving issues before they happen.


Next Steps

  1. Install Node Exporter on your VPS
  2. Deploy the collection scripts from this article
  3. Configure the LLM analysis engine
  4. Set up Cron scheduled tasks
  5. Integrate alert notifications

Remember: Security operations is not a one-time task — it’s a continuous habit. Let AI Agent be your 24/7 operations assistant.

📺 看视频版教程 → DuckDB Lab YouTube

Subscribe for more DuckDB & AI automation tutorials