How to Monitor VPS Performance with Grafana and Prometheus
UptimeRobot tells you if your site is up or down. Netdata shows you what’s happening right now. But Grafana + Prometheus answers the harder questions: Why did the server slow down last Tuesday at 2 AM? What was the memory trend over the past 30 days before the OOM crash? Which API endpoint is consuming the most CPU over time?
This guide sets up a complete Prometheus + Grafana monitoring stack on your VPS: metric collection with Node Exporter, long-term storage in Prometheus, beautiful dashboards in Grafana, and alerting when things go wrong.
The Monitoring Stack
| Component | Role |
|---|---|
| Node Exporter | Exposes Linux server metrics (CPU, RAM, disk, network) as HTTP endpoints |
| Prometheus | Time-series database that scrapes and stores metrics |
| Grafana | Visualization layer β turns Prometheus data into dashboards |
| Alertmanager | Routes alerts (email, Telegram, PagerDuty) when thresholds are breached |
π‘ Resource note: The full stack uses ~300β500 MB RAM. Run it on a dedicated monitoring VPS or alongside other apps on a 4 GB+ plan. View VPS Plans β
Step 1: Install Node Exporter (on each server to monitor)
NODE_EXPORTER_VERSION="1.8.2"
wget https://github.com/prometheus/node_exporter/releases/download/v${NODE_EXPORTER_VERSION}/node_exporter-${NODE_EXPORTER_VERSION}.linux-amd64.tar.gz
tar xvfz node_exporter-*.tar.gz
sudo mv node_exporter-*/node_exporter /usr/local/bin/
sudo useradd -rs /bin/false node_exporter
sudo nano /etc/systemd/system/node_exporter.service
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter \
--collector.systemd \
--collector.processes
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable node_exporter
sudo systemctl start node_exporter
# Verify metrics are being exported
curl http://localhost:9100/metrics | head -20
Secure Node Exporter (don’t expose publicly)
# Node Exporter should only be accessible locally or via SSH tunnel
# Add to UFW: allow only from Prometheus server IP
sudo ufw allow from PROMETHEUS_SERVER_IP to any port 9100
Step 2: Install Prometheus
PROMETHEUS_VERSION="2.54.1"
wget https://github.com/prometheus/prometheus/releases/download/v${PROMETHEUS_VERSION}/prometheus-${PROMETHEUS_VERSION}.linux-amd64.tar.gz
tar xvfz prometheus-*.tar.gz
sudo mv prometheus-*/prometheus /usr/local/bin/
sudo mv prometheus-*/promtool /usr/local/bin/
sudo useradd -rs /bin/false prometheus
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /var/lib/prometheus
Configure Prometheus
sudo nano /etc/prometheus/prometheus.yml
global:
scrape_interval: 15s # How often to collect metrics
evaluation_interval: 15s # How often to evaluate alert rules
alerting:
alertmanagers:
- static_configs:
- targets:
- localhost:9093
rule_files:
- "/etc/prometheus/alerts/*.yml"
scrape_configs:
# Monitor the Prometheus server itself
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Monitor this VPS (local Node Exporter)
- job_name: 'vps-main'
static_configs:
- targets: ['localhost:9100']
labels:
server: 'main-vps'
location: 'usa'
# Monitor additional VPS instances
- job_name: 'vps-hk'
static_configs:
- targets: ['HK_VPS_IP:9100']
labels:
server: 'hk-vps'
location: 'hong-kong'
- job_name: 'vps-backup'
static_configs:
- targets: ['BACKUP_VPS_IP:9100']
labels:
server: 'backup-vps'
location: 'usa'
Create Prometheus systemd service
sudo nano /etc/systemd/system/prometheus.service
[Unit]
Description=Prometheus Monitoring
After=network.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/var/lib/prometheus \
--storage.tsdb.retention.time=30d \
--web.listen-address=127.0.0.1:9090
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable prometheus
sudo systemctl start prometheus
Step 3: Install Grafana
sudo apt install -y apt-transport-https software-properties-common wget
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update && sudo apt install grafana -y
sudo systemctl enable grafana-server
sudo systemctl start grafana-server
Expose Grafana via Nginx with SSL
sudo nano /etc/nginx/sites-available/grafana
server {
listen 80;
server_name monitor.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name monitor.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/monitor.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/monitor.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
}
}
sudo ln -s /etc/nginx/sites-available/grafana /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d monitor.yourdomain.com
Step 4: Configure Grafana
First login
- Open
https://monitor.yourdomain.com - Login with
admin/adminβ change password immediately
Add Prometheus as a data source
- Grafana β Connections β Data sources β Add data source
- Select Prometheus
- URL:
http://localhost:9090 - Click Save & test β should show “Successfully queried the Prometheus API”
Import the Node Exporter Full dashboard
- Grafana β Dashboards β Import
- Enter dashboard ID:
1860(Node Exporter Full β most popular community dashboard) - Select your Prometheus data source
- Click Import
You now have a professional server monitoring dashboard showing CPU usage, memory, disk I/O, network traffic, system load, and more β all with historical data going back 30 days. β
Step 5: Create Custom Dashboards
Key PromQL queries for VPS monitoring
# CPU usage percentage (all cores combined)
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Memory usage percentage
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
# Disk usage percentage for root partition
(1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})) * 100
# Network receive rate (bytes per second)
rate(node_network_receive_bytes_total{device="eth0"}[5m])
# Number of open file descriptors
node_filefd_allocated
# Load average (1-minute)
node_load1
Step 6: Set Up Alerting
Install Alertmanager
ALERTMANAGER_VERSION="0.27.0"
wget https://github.com/prometheus/alertmanager/releases/download/v${ALERTMANAGER_VERSION}/alertmanager-${ALERTMANAGER_VERSION}.linux-amd64.tar.gz
tar xvfz alertmanager-*.tar.gz
sudo mv alertmanager-*/alertmanager /usr/local/bin/
sudo mv alertmanager-*/amtool /usr/local/bin/
sudo mkdir -p /etc/alertmanager
sudo nano /etc/alertmanager/alertmanager.yml
global:
smtp_from: 'alerts@yourdomain.com'
smtp_smarthost: 'smtp.gmail.com:587'
smtp_auth_username: 'alerts@yourdomain.com'
smtp_auth_password: 'your-app-password'
route:
group_by: ['alertname', 'server']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: 'email-alerts'
receivers:
- name: 'email-alerts'
email_configs:
- to: 'you@yourdomain.com'
send_resolved: true
Create alert rules
sudo mkdir -p /etc/prometheus/alerts
sudo nano /etc/prometheus/alerts/vps.yml
groups:
- name: vps_alerts
rules:
# High CPU alert
- alert: HighCPUUsage
expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
for: 5m
labels:
severity: warning
annotations:
summary: "High CPU usage on {{ $labels.instance }}"
description: "CPU usage is {{ $value }}% (threshold: 85%)"
# High memory alert
- alert: HighMemoryUsage
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90
for: 5m
labels:
severity: critical
annotations:
summary: "High memory usage on {{ $labels.instance }}"
description: "Memory usage is {{ $value }}%"
# Disk space alert
- alert: DiskSpaceLow
expr: (1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})) * 100 > 80
for: 10m
labels:
severity: warning
annotations:
summary: "Low disk space on {{ $labels.instance }}"
description: "Disk is {{ $value }}% full"
# Server down alert
- alert: ServerDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Server {{ $labels.instance }} is down"
sudo systemctl restart prometheus
Step 7: Add Telegram Alerting
sudo nano /etc/alertmanager/alertmanager.yml
receivers:
- name: 'telegram-alerts'
telegram_configs:
- bot_token: 'YOUR_TELEGRAM_BOT_TOKEN'
chat_id: YOUR_CHAT_ID
message: |
π¨ *{{ .Status | toUpper }}*: {{ .CommonLabels.alertname }}
Server: {{ .CommonLabels.instance }}
{{ .CommonAnnotations.description }}
Step 8: Monitor Multiple Servers
The real power of Prometheus is monitoring your entire infrastructure from one place. Install Node Exporter on every VPS, add them to prometheus.yml, and they all appear in your Grafana dashboards with server labels for easy filtering.
# In prometheus.yml, add each new server:
- job_name: 'client-server-1'
static_configs:
- targets: ['CLIENT_IP:9100']
labels:
server: 'client-1'
environment: 'production'
Create a Grafana variable called $server to filter dashboards by server β switch between monitoring your USA VPS, HK VPS, and client servers with a single dropdown.
Final Thoughts
Prometheus + Grafana is the industry standard for infrastructure monitoring precisely because it scales from one VPS to thousands of servers without changing the architecture. The 30-day metric retention lets you analyze trends and diagnose past incidents. The alerting pipeline ensures you’re notified before problems become outages.
- πΊπΈ USA VPS β from $20/month
- ππ° Hong Kong VPS β CN2+BGP routing