What Is Node Exporter and Why Do You Need It?
Prometheus is great at collecting and storing time-series metrics, but it can’t read Linux hardware or OS information on its own. That’s where Node Exporter comes in — it runs on each server, reads system data, and exposes an HTTP endpoint for Prometheus to scrape periodically.
Simply put: Node Exporter is an “agent” that sits on each machine, reads Linux’s /proc and /sys filesystems, and translates that data into a format Prometheus understands.
My monitoring setup runs Prometheus + Grafana across 15 servers, and this stack has helped catch incidents before users even notice. For example, a staging server’s disk hit 94% at 2 AM — Alertmanager fired a Telegram alert immediately, and we handled it before the workday started. It all begins with a proper Node Exporter installation.
This article focuses specifically on Node Exporter: how to install it, which collectors are worth enabling, and how to verify it’s running correctly. Connecting Prometheus and Grafana is covered in a separate post.
Installing Node Exporter
Method 1: Manual Installation from Binary (Recommended)
This approach gives you full version control and works on any distro. Visit the Node Exporter releases page to grab the latest version link:
# Download Node Exporter (replace version as needed)
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz
# Extract the archive
tar xvf node_exporter-1.8.2.linux-amd64.tar.gz
# Copy the binary to /usr/local/bin
sudo cp node_exporter-1.8.2.linux-amd64/node_exporter /usr/local/bin/
sudo chmod +x /usr/local/bin/node_exporter
Create a Dedicated Service User
Running Node Exporter as root is a bad idea. Create a system user with no shell and no home directory:
sudo useradd --no-create-home --shell /bin/false node_exporter
Create a systemd Service
With a service unit, Node Exporter will start automatically after reboot and is easy to manage with systemctl:
sudo nano /etc/systemd/system/node_exporter.service
File contents:
[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=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
# Reload and enable the service
sudo systemctl daemon-reload
sudo systemctl enable node_exporter
sudo systemctl start node_exporter
# Check service status
sudo systemctl status node_exporter
Method 2: Install via Package Manager (Ubuntu/Debian)
Some distros ship a package for Node Exporter, but the version is often significantly behind upstream:
# Ubuntu 22.04+
sudo apt install prometheus-node-exporter -y
# Service is created and started automatically
sudo systemctl status prometheus-node-exporter
The downside is that the binary lives at /usr/bin/prometheus-node-exporter instead of /usr/local/bin/node_exporter, and the service name differs too. When your team works across multiple distros, this inconsistency makes debugging confusing. I generally prefer the manual install to keep all servers uniform.
Configuring Collectors
Node Exporter ships with over 50 collectors, and the most important ones are enabled by default. A few useful collectors need to be activated manually.
Default Collectors (Always Enabled)
- cpu — CPU usage, iowait, steal time
- meminfo — RAM total, used, buffers, cached
- diskstats — Disk I/O (read/write ops, throughput)
- filesystem — Disk space usage per mount point
- netdev — Network interface statistics
- loadavg — 1/5/15-minute load averages
- stat — Kernel statistics (context switches, interrupts)
Additional Collectors to Enable
Add these to the ExecStart line in your systemd unit file:
ExecStart=/usr/local/bin/node_exporter \
--collector.systemd \
--collector.processes \
--collector.interrupts \
--collector.tcpstat \
--collector.cpu.info
What each option does:
--collector.systemd— Tracks systemd service states (active/failed). Without this, you can’t alert when a service crashes.--collector.processes— Number of running processes and zombie processes--collector.tcpstat— TCP connection states (ESTABLISHED, TIME_WAIT, etc.)--collector.cpu.info— CPU model and frequency information
Disabling Unnecessary Collectors
To reduce the volume of metrics Prometheus has to process, disable collectors you don’t use:
ExecStart=/usr/local/bin/node_exporter \
--no-collector.infiniband \
--no-collector.ipvs \
--collector.systemd
Configuring Network Binding
By default, Node Exporter listens on all interfaces on port 9100. In production, bind it to the internal interface only:
ExecStart=/usr/local/bin/node_exporter \
--web.listen-address="10.0.0.5:9100" \
--collector.systemd
Replace 10.0.0.5 with the server’s internal IP, then reload:
sudo systemctl daemon-reload
sudo systemctl restart node_exporter
Verification and Monitoring
Confirm Node Exporter Is Running
# Check which port is listening
ss -tlnp | grep 9100
# Or use curl to view metrics directly
curl http://localhost:9100/metrics | head -50
Output like this means everything is working:
# HELP node_cpu_seconds_total Seconds the CPUs spent in each mode.
# TYPE node_cpu_seconds_total counter
node_cpu_seconds_total{cpu="0",mode="idle"} 12345.67
node_cpu_seconds_total{cpu="0",mode="iowait"} 23.45
Check Key Metrics
# View CPU metrics
curl -s http://localhost:9100/metrics | grep "^node_cpu"
# View disk usage
curl -s http://localhost:9100/metrics | grep "node_filesystem_avail_bytes"
# View available RAM
curl -s http://localhost:9100/metrics | grep "node_memory_MemAvailable_bytes"
Open Firewall if Needed
The Prometheus server needs to reach port 9100 on each node:
# UFW (Ubuntu)
sudo ufw allow from <prometheus-server-ip> to any port 9100
# Firewalld (CentOS/RHEL)
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="<prometheus-server-ip>" port protocol="tcp" port="9100" accept'
sudo firewall-cmd --reload
Add to Prometheus Config
On the Prometheus server, edit prometheus.yml:
scrape_configs:
- job_name: 'node'
static_configs:
- targets:
- '10.0.0.5:9100' # server 1
- '10.0.0.6:9100' # server 2
- '10.0.0.7:9100' # server 3
# Add labels for easier filtering in Grafana
relabel_configs:
- source_labels: [__address__]
target_label: instance
Reload Prometheus:
curl -X POST http://localhost:9090/-/reload
Verify in the Prometheus UI
Open your browser to http://<prometheus-server>:9090/targets — nodes showing UP in green are connected successfully. If you see DOWN, check your firewall rules and IP addresses.
Run a few PromQL queries to confirm data is coming in:
# Total CPU usage (excluding idle)
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Available RAM (GB)
node_memory_MemAvailable_bytes / 1024 / 1024 / 1024
# Remaining disk space (%)
(node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100
Common Troubleshooting
A few issues I’ve run into when setting up Node Exporter across multiple servers:
- Service won’t start: Run
journalctl -u node_exporter -n 20to check the logs — usually caused by an incorrect binary path or missing file permissions. - Port already in use: Use
ss -tlnp | grep 9100to see which process is holding the port. - Prometheus can’t scrape: Try
curl http://<node-ip>:9100/metricsfrom the Prometheus server. If it fails to connect, it’s a network/firewall issue, not a Node Exporter problem. - No systemd metrics: The
node_exporteruser needs read access to D-Bus — some distros require adding the user to thesystemd-journalgroup.
