Install Prometheus Node Exporter on VPS

Install Prometheus Node Exporter on VPS

June 2026 · 9 min read

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

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_"
Tip: If Prometheus still shows no metrics after 2-3 minutes, verify network connectivity between Prometheus server and the VPS. Use 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.