Skip to main content

VPS Monitoring

The Control Center collects and visualizes real-time VPS metrics using the systeminformation Node.js library, Socket.IO for live updates, and Recharts for visualization.


Metrics Collected

Collection Intervals

IntervalStorageRetention
3 secondsIn-memory (pushed via Socket.IO)Last 5 minutes
60 secondsSQLite MetricSnapshot table30 days

Metric Categories

CategoryMetricsSource
CPUUsage percentage (per core + total), load averagesysteminformation.currentLoad()
MemoryUsed, free, total, swap used, swap totalsysteminformation.mem()
DiskUsage per mount point, read/write IOPS, available spacesysteminformation.fsSize(), systeminformation.disksIO()
NetworkBandwidth in/out (bytes/sec), packet counts, interface statssysteminformation.networkStats()

Implementation

Metrics Collection Service

// apps/server/src/services/vps.service.ts
import si from 'systeminformation';

export const vpsService = {
async collectMetrics() {
const [cpu, memory, disk, network] = await Promise.all([
si.currentLoad(),
si.mem(),
si.fsSize(),
si.networkStats(),
]);

return {
cpu: {
total: cpu.currentLoad,
cores: cpu.cpus.map(c => c.load),
loadAvg: cpu.avgLoad,
},
memory: {
used: memory.used,
free: memory.free,
total: memory.total,
swapUsed: memory.swapused,
swapTotal: memory.swaptotal,
usagePercent: (memory.used / memory.total) * 100,
},
disk: disk.map(d => ({
mount: d.mount,
size: d.size,
used: d.used,
available: d.available,
usagePercent: d.use,
})),
network: network.map(n => ({
interface: n.iface,
rxBytes: n.rx_bytes,
txBytes: n.tx_bytes,
rxPerSec: n.rx_sec,
txPerSec: n.tx_sec,
})),
timestamp: new Date(),
};
},
};

Metrics Scheduler

// apps/server/src/services/metrics.service.ts
let collectionCount = 0;

// Collect every 3 seconds
setInterval(async () => {
const metrics = await vpsService.collectMetrics();

// Push to all connected Socket.IO clients
io.of('/metrics').emit('vps:metrics', metrics);

collectionCount++;

// Persist every 60 seconds (every 20th collection)
if (collectionCount % 20 === 0) {
await prisma.metricSnapshot.create({
data: {
cpu: metrics.cpu.total,
memory: JSON.stringify(metrics.memory),
disk: JSON.stringify(metrics.disk),
network: JSON.stringify(metrics.network),
timestamp: metrics.timestamp,
},
});
}
}, 3000);

Data Retention

// Clean up metrics older than 30 days (daily cron)
async function cleanOldMetrics() {
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
await prisma.metricSnapshot.deleteMany({
where: { timestamp: { lt: thirtyDaysAgo } },
});
}

Real-Time Delivery

Metrics are pushed to connected clients via the /metrics Socket.IO namespace:

// Server: emit metrics
io.of('/metrics').emit('vps:metrics', metrics);

// Client: receive and render
import { useEffect, useState } from 'react';
import { io } from 'socket.io-client';

function VPSMonitor() {
const [metrics, setMetrics] = useState(null);

useEffect(() => {
const socket = io('/metrics', {
auth: { token: getAccessToken() }, // JWT required
});

socket.on('vps:metrics', (data) => {
setMetrics(data);
});

return () => socket.disconnect();
}, []);

return <MetricsCharts data={metrics} />;
}

Dashboard Visualizations

ChartData SourceType
CPU Usage (real-time)Live Socket.IO streamArea chart with 5-minute window
Memory UsageLive + historicalGauge + time-series
Disk UsagePer mount pointDonut chart per mount
Network I/OLive bandwidthStacked area chart (in/out)
CPU History (30 days)MetricSnapshot tableTime-series line chart
Memory History (30 days)MetricSnapshot tableTime-series line chart

Alert Thresholds

MetricWarningCriticalAction
CPU> 80% sustained (5 min)> 95% sustained (2 min)Notification
Memory> 85% usage> 95% usageNotification
Disk> 85% on any mount> 95% on any mountNotification
NetworkUnusual spike (> 3x baseline)-Notification

Alerts are created as Notification records and pushed via the /notifications Socket.IO namespace.


Cross-Reference