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:
- Security scanning: Detect abnormal logins, exposed ports, security vulnerabilities
- Performance analysis: Identify resource bottlenecks, slow queries, memory leaks
- Capacity forecasting: Predict disk and bandwidth usage trends based on historical data
- Auto-fixing: Generate and apply repair scripts for common issues
- 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
| Component | Role | Tech Stack |
|---|---|---|
| Collection Layer | Gather metrics, logs, configs | Node Exporter, systemd journal, SSH |
| Analysis Layer | Identify anomaly patterns | Prometheus Query, LLM reasoning |
| Decision Layer | Evaluate risk, plan fixes | Policy library + LLM generation |
| Execution Layer | Run safe operations | Ansible, 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:
- Automation: Daily comprehensive health checks without manual intervention
- Intelligence: LLM-powered understanding of complex patterns with natural language reports
- Proactiveness: Early detection of potential issues before they become incidents
- Actionability: Auto-generated fix scripts, one-click deployment
- 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
- Install Node Exporter on your VPS
- Deploy the collection scripts from this article
- Configure the LLM analysis engine
- Set up Cron scheduled tasks
- 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.
