Where to start First steps track · 15 stages
Linux Labs

node_exporter: machine metrics for Prometheus

14 min Ubuntu 24.04 · node_exporter 1.7

The Prometheus exporter for the machine itself: serves CPU, memory, disk, network and service metrics on :9100.

Cheat sheet

8 commands Printable version
  • sudo systemctl enable --now prometheus-node-exporter turn it on
  • systemctl status prometheus-node-exporter is the service alive
  • ss -tlnp | grep 9100 where it listens
  • curl -s localhost:9100/metrics | less everything it serves
  • curl -s localhost:9100/metrics | grep -c '^node_' value count
  • curl -s localhost:9100/metrics | grep MemAvailable one metric
  • curl -s localhost:9100/metrics | promtool check metrics format
  • sudo systemctl restart prometheus-node-exporter after editing ARGS

Overview

node_exporter is the Prometheus exporter for the machine itself. It reads /proc, /sys and the systemd state and serves current values over HTTP on port 9100, path /metrics: CPU, memory, disks, file systems, network, load and service state. It stores nothing and sends nothing anywhere: Prometheus comes for the metrics on a schedule (prometheus).

In Ubuntu this is the prometheus-node-exporter package: a service of the same name, the program /usr/bin/prometheus-node-exporter, and the prometheus user. On the training machine the package is installed and the service is off:

bash
sudo systemctl enable --now prometheus-node-exporter

The exporter answers a second or two after it starts.

Exposition format

The /metrics answer is text. Each metric has two comment lines and value lines:

text
$ curl -s localhost:9100/metrics | grep MemAvailable
# HELP node_memory_MemAvailable_bytes Memory information field
  MemAvailable_bytes.
# TYPE node_memory_MemAvailable_bytes gauge
node_memory_MemAvailable_bytes 2.95616512e+08

The first line of the output is a single line, wrapped here.

Line What is in it
# HELP name text what the metric measures
# TYPE name type counter, gauge, histogram, summary or untyped
name{labels} value one value: a series with its own label set

The value is a floating-point number, often in exponent notation: 2.95616512e+08 is 295,616,512 bytes, about 282 MiB. A metric with labels has several value lines:

text
node_filesystem_avail_bytes{device="/dev/vda4",fstype="ext4",
  mountpoint="/"} 5.361012736e+09

The name tells the units: _bytes, _seconds; _total marks a counter (promql). The node_ prefix is machine metrics, go_ and process_ are about the exporter itself. On the training machine the answer is about 1900 lines: roughly 270 metrics and 1300 node_* values.

Main metrics

Metric Meaning
node_cpu_seconds_total CPU seconds since boot by core (cpu) and mode (mode: idle, user, system, iowait…)
node_load1, node_load5, node_load15 load average over 1, 5 and 15 minutes
node_memory_MemAvailable_bytes memory that can be taken without swapping out; next to it are MemTotal, MemFree and the other /proc/meminfo fields
node_filesystem_avail_bytes free space on the file system for ordinary users, like the Avail column of df
node_filesystem_size_bytes file system size
node_disk_read_bytes_total, node_disk_written_bytes_total bytes read from and written to a disk (device)
node_network_receive_bytes_total, node_network_transmit_bytes_total bytes received and sent through an interface (device)
node_systemd_unit_state 1 for the unit's current state (name, state), 0 for the others
node_boot_time_seconds boot time in Unix seconds
node_uname_info kernel version and host name in labels, value 1

How much memory can be taken is MemAvailable. MemFree is memory used by nothing at all; the file cache is not in it, although the kernel gives the cache back on demand. On the training machine MemFree is about 13 MiB and MemAvailable about 280. More on load and memory: load-average and memory-swap.

Collectors

A collector is the part of the exporter that reads one source: meminfo reads /proc/meminfo, filesystem the mounted file systems, netdev the interfaces, systemd the units, textfile the files with your own metrics. More than forty are on on the training machine. Which ones, and whether the last read worked, is in the node_scrape_collector_success metric:

bash
curl -s localhost:9100/metrics | grep '^node_scrape_collector_success'

0 for a collector means its source could not be read; the reason is in the service journal. The --collector.<name> and --no-collector.<name> flags turn a collector on or off (see "Configuration").

The filesystem collector does not serve node_filesystem_* for every mount point. It skips whatever matches --collector.filesystem.mount-points-exclude, which on the training machine defaults to:

text
^/(dev|proc|run|sys|mnt|media|var/lib/docker/.+|
  var/lib/containers/storage/.+)($|/)

The expression is wrapped here. A volume mounted under /mnt/… or /media/… does not get into the metrics at all: it has no series, and a rule with its mountpoint is empty (node-exporter). File system types are cut by --collector.filesystem.fs-types-exclude (tmpfs is not in that list; proc, sysfs, overlay, squashfs are). A flag in ARGS sets your own list and replaces the default.

textfile: your own metrics in a file

The textfile collector reads the *.prom files in /var/lib/prometheus/node-exporter on every request to /metrics and serves their lines along with its own. This is how a script with no HTTP server of its own tells Prometheus, for example, the time of the last successful backup:

text
$ echo 'lab_backup_last_success_timestamp_seconds 1792640000' \
    | sudo tee /var/lib/prometheus/node-exporter/backup.prom
$ curl -s localhost:9100/metrics | grep lab_backup
# HELP lab_backup_last_success_timestamp_seconds Metric read from
  /var/lib/prometheus/node-exporter/backup.prom
# TYPE lab_backup_last_success_timestamp_seconds untyped
lab_backup_last_success_timestamp_seconds 1.79264e+09

# HELP and # TYPE lines in the file are optional: without them the exporter writes its own, and the type is untyped. The directory belongs to root, so writing to it takes sudo.

Configuration

9

Startup flags

The exporter has no settings file, only command-line flags. The service takes them from the ARGS variable in /etc/default/prometheus-node-exporter; the package leaves it empty (ARGS=""), so everything is at its default:

bash
# /etc/default/prometheus-node-exporter
ARGS="--web.listen-address=127.0.0.1:9100 --no-collector.zfs"

After an edit, run sudo systemctl restart prometheus-node-exporter. reload does not fit here: see node-exporter. All flags with descriptions: prometheus-node-exporter --help.

Address and port

By default the exporter listens on port 9100 on every address of the machine:

text
$ ss -tlnp | grep 9100
LISTEN 0  4096  *:9100  *:*  users:(("prometheus-node",pid=1178,fd=3))

The --web.listen-address flag sets the address and port. It goes into ARGS in /etc/default/prometheus-node-exporter or into a service drop-in (sudo systemctl edit prometheus-node-exporter → the file /etc/systemd/system/prometheus-node-exporter.service.d/override.conf, where an ExecStart= line replaces the start command). Where the flag is actually set:

  • systemctl cat prometheus-node-exporter shows the unit with its drop-ins, each file under a # /path line;
  • systemctl status prometheus-node-exporter shows the whole command with its flags in the CGroup line under the process number;
  • ss -tlnp | grep prometheus-node shows which port it listens on now.

There is no password and no encryption: whoever reaches the port sees the metrics. --web.listen-address=127.0.0.1:9100 leaves access from the machine itself only, and Prometheus on another machine then gets connection refused. When Prometheus lives on another machine, the exporter listens on its external address, and the firewall opens the port to the Prometheus address only.

Metrics are served only on the /metrics path. On any other path the exporter answers with an HTML page and code 200, and Prometheus with a wrong metrics_path writes expected a valid start token, got "<" into lastError (prometheus).

Directive What it does Example
--web.listen-address ARGS The address and port where the exporter waits for Prometheus; without an address, every address of the machine. --web.listen-address=127.0.0.1:9100
--web.telemetry-path ARGS The path that serves the metrics. --web.telemetry-path=/metrics
--collector.<имя> ARGS Turn on a collector that is off by default. --collector.processes
--no-collector.<имя> ARGS Turn off a collector that is on by default. --no-collector.zfs
--collector.disable-defaults ARGS Turn off every default collector; the needed ones are turned on with --collector.<name>. --collector.disable-defaults --collector.meminfo
--collector.textfile.directory ARGS The directory of *.prom files the exporter serves along with its own metrics. --collector.textfile.directory=/var/lib/prometheus/node-exporter
--collector.netdev.device-exclude ARGS A regular expression of network interfaces left out of node_network_*. --collector.netdev.device-exclude=^(lo|veth.*)$
--collector.filesystem.mount-points-exclude ARGS A regular expression of mount points that get no node_filesystem_*; /mnt and /media are in it by default. --collector.filesystem.mount-points-exclude=^/(dev|proc|run|sys)($|/)
--collector.systemd.unit-include ARGS A regular expression of units whose state goes into node_systemd_unit_state. --collector.systemd.unit-include=(nginx|ssh)\.service

Nothing found. Try a shorter query.

Recipes

4

Check the exporter on the machine itself

bash
systemctl is-active prometheus-node-exporter
ss -tlnp | grep 9100
curl -s localhost:9100/metrics | grep '^node_load1 '

active, a LISTEN line and a node_load1 value mean the exporter is fine. Next, check whether Prometheus collects it: prometheus.

Collect a second machine

On node2, turn on the exporter and check that it listens on more than 127.0.0.1. On node1, add a target to the node job and reload the config:

yaml
scrape_configs:
  - job_name: node
    static_configs:
      - targets: ['localhost:9100', 'node2:9100']
bash
promtool check config /etc/prometheus/prometheus.yml
sudo systemctl reload prometheus

After one scrape interval up{instance="node2:9100"} should become 1. If it is 0, the target's lastError names the cause (prometheus).

A metric from your own script

Write to a temporary file in the same directory and rename it: mv swaps the file as a whole, so the exporter never reads it half written.

bash
d=/var/lib/prometheus/node-exporter
m=backup_last_success_timestamp_seconds
echo "$m $(date +%s)" > "$d/backup.tmp"
mv "$d/backup.tmp" "$d/backup.prom"

The script runs as root: the directory belongs to it. The exporter skips files without the .prom extension. A "no backup for over a day" rule is time() - backup_last_success_timestamp_seconds > 86400 (promql).

Service state in metrics

text
$ curl -s localhost:9100/metrics \
    | grep 'node_systemd_unit_state{name="ssh.service"'
node_systemd_unit_state{name="ssh.service",state="activating",
  type="notify"} 0
node_systemd_unit_state{name="ssh.service",state="active",
  type="notify"} 1
…

Each output line is wrapped here. A service that is not running: node_systemd_unit_state{name="nginx.service",state="active"} == 0.

Troubleshooting

6

connection refused on port 9100

curl localhost:9100/metrics answers Failed to connect, and the target in Prometheus is down with the error:

text
Get "http://localhost:9100/metrics": dial tcp 127.0.0.1:9100:
  connect: connection refused

Nobody listens on the port. Check systemctl status prometheus-node-exporter, then ss -tlnp | grep prometheus-node: the exporter may listen on another port or on 127.0.0.1 only while Prometheus goes to the external address.

context deadline exceeded

Prometheus did not get an answer within scrape_timeout (10 seconds by default). Usually a firewall on the exporter's machine silently drops the packets: connection refused would have come at once. Check the firewall rules for port 9100.

reload stopped the exporter

text
$ sudo systemctl reload prometheus-node-exporter
$ systemctl is-active prometheus-node-exporter
inactive

The journal says Deactivated successfully. The exporter has nothing to reread: the signal reload sends makes it exit. restart applies the ARGS flags, and start brings a stopped exporter back.

No node_network_* metrics

curl -s localhost:9100/metrics | grep node_network_receive is empty, and rate(node_network_receive_bytes_total[1m]) returns nothing. A single-VM machine has only the lo interface, and the exporter skips it: --collector.netdev.device-exclude defaults to ^lo$. A machine with a network interface has the series, one per device.

No series for a mount point

node_filesystem_avail_bytes{mountpoint="/mnt/data"} is empty although df sees the volume. The mount point matches --collector.filesystem.mount-points-exclude, which by default covers /mnt, /media, /run and more (node-exporter). Set your own expression in ARGS, for example --collector.filesystem.mount-points-exclude=^/(dev|proc|run|sys)($|/), and restart the exporter. Which mount points it sees: curl -s localhost:9100/metrics | grep '^node_filesystem_size_bytes'.

node_textfile_scrape_error 1

A file in the textfile directory could not be parsed. Which one and why is in the journal:

text
$ journalctl -u prometheus-node-exporter -o cat | grep textfile
… level=error collector=textfile msg="failed to collect textfile data"
  file=bad.prom err="failed to parse textfile data from
  \"/var/lib/prometheus/node-exporter/bad.prom\": text format parsing
  error in line 1: expected float as value, got \"metric\""

A value line is a name, a space and a number: name 42 or name{label="x"} 42. The exporter serves the other files of the directory as usual.

Danger zone

1

Port 9100 open to everyone

The metrics tell a lot about the machine: the kernel version, mount points, the list of services, network interfaces. An exporter on *:9100 without a firewall, on a machine with a public address, gives all this to anyone. Limit access with the listen address or a firewall rule.

Terms

1
exporter
A program that serves the metrics of a system or service over HTTP in the Prometheus text format, usually on the /metrics path. For the machine itself it is node_exporter on port 9100. Also appears in: Prometheus: metric collection and alerting rules, PromQL: the Prometheus query language, Firewall, Alertmanager: who gets alerts and how often and 1 more

See also

  • prometheus — who collects these metrics and how.
  • promql — how to turn them into rates, ratios and thresholds.
  • system-monitoring — the same quantities with top, free, iostat.
  • firewall — open port 9100 to Prometheus only.
  • ss — which address and port the exporter listens on.
  • systemctl — enable --now, restart, status.
  • load-average — what node_load1 and its neighbours mean.
  • curl — request /metrics from the terminal.
Loading…