Install Prometheus Node Exporter on VPS
Node Exporter is a lightweight monitoring agent that collects VPS metrics (CPU, RAM, Disk, Network, etc.) and continuously exports them to Prometheus — install once, it runs in the background and pushes telemetry data automatically. From there, Prometheus stores the time-series data, Grafana visualizes it with beautiful dashboards, and AlertManager sends notifications when thresholds breach. Unlike Netdata (all-in-one), Node Exporter is modular — you choose which collectors to enable and which to skip. It has fewer moving parts and a smaller attack surface because it only collects metrics and has no built-in dashboard. This guide covers installing Node Exporter with systemd, configuring Prometheus to scrape it, setting up security with HTTPS reverse proxy, and troubleshooting common issues.
Understanding the Prometheus Stack Architecture
- Node Exporter: Agent installed on each VPS that collects system metrics: CPU, RAM, Disk, Network, Load Average
- Prometheus: Central server that periodically (every 15 seconds by default) pulls metrics from Node Exporter and stores them as time-series data
- Grafana: Frontend that queries Prometheus and renders metrics into dashboards with graphs, gauges, and heatmaps
- AlertManager: Sends alerts to Email, Slack, Telegram, or Line when Prometheus rules trigger
- Metrics Format: Each metric has a name like
node_cpu_seconds_total,node_memory_MemFree_bytes,node_disk_io_reads_completed_total
Download and Install Node Exporter Binary
# Download the latest Node Exporter release
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.1/node_exporter-1.8.1.linux-amd64.tar.gz
# Extract the archive
tar xzvf node_exporter-1.8.1.linux-amd64.tar.gz
cd node_exporter-1.8.1.linux-amd64
# Copy binary to system PATH
sudo cp node_exporter /usr/local/bin/
sudo chmod +x /usr/local/bin/node_exporter
# Verify installation
node_exporter --version
Create Systemd Service for Node Exporter
# Create a new systemd unit file
sudo nano /etc/systemd/system/node_exporter.service
# Paste the following content:
[Unit]
Description=Prometheus Node Exporter
After=network.target
[Service]
Type=simple
User=node_exporter
Group=node_exporter
WorkingDirectory=/var/lib/node_exporter
ExecStart=/usr/local/bin/node_exporter \
--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/) \
--collector.netdev.device-exclude=^(veth.*)$
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
# Save file (Ctrl+O, Enter, Ctrl+X)
Create Node Exporter System User
# Create unprivileged user with no shell (security best practice)
sudo useradd --no-create-home --shell /bin/false node_exporter
# Create working directory
sudo mkdir -p /var/lib/node_exporter
sudo chown -R node_exporter:node_exporter /var/lib/node_exporter
# Reload systemd daemon to recognize the new service
sudo systemctl daemon-reload
# Enable Node Exporter to start on boot
sudo systemctl enable node_exporter
# Start the service
sudo systemctl start node_exporter
# Check status
sudo systemctl status node_exporter
Configure Firewall and Reverse Proxy
# Node Exporter listens on port 9100 by default. Open it in firewall:
# For UFW:
sudo ufw allow 9100/tcp
# Optional: Set up Nginx reverse proxy with HTTPS (for remote access):
sudo nano /etc/nginx/sites-available/metrics
server {
listen 443 ssl;
server_name metrics.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/metrics.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/metrics.yourdomain.com/privkey.pem;
auth_basic "Metrics";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass http://127.0.0.1:9100;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# Create HTTP basic auth file:
sudo apt-get install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd prometheus
# Enter password when prompted
# Test Nginx configuration
sudo nginx -t
# Reload Nginx
sudo systemctl reload nginx
Configure Prometheus to Scrape Node Exporter
# Edit prometheus.yml on your Prometheus server
sudo nano /etc/prometheus/prometheus.yml
# Add a new scrape job:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
static_configs:
# For Node Exporter on same server
- targets: ['localhost:9100']
labels:
instance: 'my-vps-prod'
# For remote Node Exporter:
- targets: ['192.168.1.50:9100']
labels:
instance: 'vps-staging'
# With HTTPS and basic auth
- targets: ['https://metrics.yourdomain.com:443']
scheme: https
basic_auth:
username: prometheus
password: 'YOUR_PASSWORD'
labels:
instance: 'vps-remote'
# Reload Prometheus:
sudo systemctl reload prometheus
# Open Prometheus UI at http://localhost:9090
# Go to Status → Targets to verify Node Exporter targets are UP
Query Metrics from Prometheus
# Open Prometheus UI at http://localhost:9090/graph
# CPU usage percentage
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Memory free in GB
node_memory_MemFree_bytes / (1024^3)
# Memory used percentage
100 - ((node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes) / node_memory_MemTotal_bytes * 100)
# Disk usage percentage
100 - (node_filesystem_avail_bytes{fstype!~"tmpfs|fuse.lxcfs|squashfs"} / node_filesystem_size_bytes{fstype!~"tmpfs|fuse.lxcfs|squashfs"} * 100)
# System load average (1 minute)
node_load1
# Network transmission rate (bytes/sec)
rate(node_network_transmit_bytes_total{instance="my-vps-prod"}[5m])
Verify Node Exporter is Working Correctly
# Check service status
systemctl status node_exporter
# View recent service logs
sudo journalctl -u node_exporter -f
# Test the metrics endpoint
curl http://localhost:9100/metrics | head -30
# Check for a specific metric
curl -s http://localhost:9100/metrics | grep node_cpu_seconds_total
# Verify metric count after config changes
sudo systemctl restart node_exporter && sleep 2 && curl -s http://localhost:9100/metrics | grep -c "^node_"
telnet VPS_IP 9100 to test the connection. Then wait 30-45 seconds for the next scrape interval — Prometheus pulls metrics at the interval defined in its configuration (default 15 seconds).Troubleshooting Common Issues
Error: "connection refused" when Prometheus scrapes metrics
Node Exporter is not running, port 9100 is blocked by firewall, or the target hostname is incorrect in Prometheus config. Verify the service is up and accessible.
# Check if Node Exporter service is running
sudo systemctl status node_exporter
# Verify port is listening
sudo netstat -tlnp | grep 9100
# Should show node_exporter listening on 0.0.0.0:9100 or 127.0.0.1:9100
# If not running, restart it
sudo systemctl restart node_exporter
# Open firewall
sudo ufw allow 9100/tcp
# Test connectivity from Prometheus server
telnet PROMETHEUS_SERVER_IP VPS_IP 9100
Metrics show "NaN" or "stale" in Prometheus
Prometheus is unable to scrape Node Exporter consistently. Causes: unstable network, Prometheus scrape timeout too short, or Node Exporter responding slowly. Increase the timeout or scrape interval.
# Increase scrape timeout in prometheus.yml:
scrape_configs:
- job_name: 'node'
scrape_timeout: 30s # default is 10s
scrape_interval: 30s
static_configs:
- targets: ['VPS_IP:9100']
# Reload Prometheus
sudo systemctl restart prometheus
# Check the number of metrics exported (too many can overload):
curl -s http://VPS_IP:9100/metrics | wc -l
# If > 5000 metrics, disable unused collectors
Node Exporter consuming excessive memory
Some collectors (like filesystem and textfile) may scan many files and consume memory. Disable collectors you don't need.
# Edit /etc/systemd/system/node_exporter.service:
ExecStart=/usr/local/bin/node_exporter \
--no-collector.arp \
--no-collector.edac \
--no-collector.ipvs \
--collector.netdev.device-exclude=docker|veth
# Reload and restart
sudo systemctl daemon-reload
sudo systemctl restart node_exporter
HTTPS certificate error with reverse proxy
Prometheus cannot verify the SSL certificate presented by the reverse proxy, or the certificate is invalid.
# In prometheus.yml, skip certificate verification (dev/staging only):
scrape_configs:
- job_name: 'node'
scheme: https
tls_config:
insecure_skip_verify: true # Never use in production
basic_auth:
username: prometheus
password: 'PASSWORD'
static_configs:
- targets: ['metrics.yourdomain.com:443']
Frequently Asked Questions
How is Node Exporter different from cAdvisor?
Node Exporter collects system-level OS metrics: CPU, RAM, Disk, Processes of the entire host. cAdvisor collects container-specific metrics: memory and CPU usage of individual Docker containers. If your VPS runs Docker, use both — Node Exporter for host metrics and cAdvisor for container metrics. They complement each other.
Is TLS/HTTPS required for Node Exporter?
Not if Node Exporter and Prometheus run on the same server or on a private network. However, if metrics are exposed over the internet or contain non-public information (IP addresses, hostnames), secure it with TLS + HTTP basic auth. See the Nginx reverse proxy example above for a complete setup.
How long does Prometheus retain metrics?
Prometheus stores data for as long as disk space allows. The default retention policy is 15 days (configurable via the --storage.tsdb.retention flag in prometheus.yml). Node Exporter itself has no historical storage — it only exposes current counters and gauges. Prometheus is responsible for time-series storage and history.