FTMON Exchange

hardware · nagios

Hardware temperature

Alert when the hottest thermal zone exceeds Celsius thresholds, with temperature history.

Why

FTMON's built-ins cover load, CPU hogs and memory pressure, but not board or package temperature. Sustained heat is an early signal of fan failure, dust, aggressive workloads, or a dying sensor path. This recipe uses check_temperature from nagios-plugins-linux so FTMON can confirm over-temp states and keep Celsius history for the hottest thermal zone.

Install

Upstream project (do not vendor plugins into FTMON): https://github.com/madrisan/nagios-plugins-linux

The check needs a readable /sys/class/thermal tree. On Ubuntu/Debian there is no complete apt package for the full suite; build a release into the usual plugin directory:

git clone --branch v35 --depth 1 \
  https://github.com/madrisan/nagios-plugins-linux.git
cd nagios-plugins-linux
autoreconf --install
./configure --libexecdir=/usr/lib/nagios/plugins
make
sudo make install
/usr/lib/nagios/plugins/check_temperature -V

Gentoo can install net-analyzer/nagios-plugins-linux-madrisan instead. Other distributions may ship distro-built packages from the project's packages/ targets.

Configure

ftmon recipe install temperature

Defaults warn at **80°C** and go critical at **90°C** for the hottest zone (plugin behaviour when -t is omitted). Brief turbo spikes are common, so warning and critical rules confirm for **three** cycles before notifying.

Separately, a **Trends** growth profile tracks sustained climb: over a **2 h** window, if the fitted slope exceeds **8 °C/h** with mostly rising samples, FTMON warns (group thermal-rise). That catches fan failure / stuck load warming while still below the absolute plugin thresholds. The dashboard tile shows the hottest current temperature beside the warning and critical values. Tune rise_celsius_per_h and rise_confidence_min in the monitor TOML.

The absolute values are deliberately present in two declarative places: -w/-c in checks.toml control plugin alert state, while warn_celsius and crit_celsius in the monitor TOML label the dashboard readout. Change both copies together so the displayed thresholds continue to describe the plugin.

To pin a stable sensor (package temp, ACPI zone, …), list zones and edit the registry argv:

/usr/lib/nagios/plugins/check_temperature --list
# then add: "-t", "<zone-number>"

The monitor entity stays thermal:max so history remains one series when the hottest zone changes between polls. Raise or lower -w/-c in checks.toml for your chassis and update the matching monitor parameters; leave FTMON's rule expressions alone unless you change groups.

Test

/usr/lib/nagios/plugins/check_temperature -w 80 -c 90
echo "$?"

Exit states are 0 OK, 1 warning, 2 critical, and 3 unknown. Fixtures under fixtures/ match observed nagios-plugins-linux v35 first-line output (including temp=…C perfdata). Unknown coverage uses a missing zone:

/usr/lib/nagios/plugins/check_temperature -t 999
ftmon check
ftmon doctor

Security and permissions

No elevated privileges: the plugin only reads sysfs thermal nodes. It does not contact the network. On locked-down hosts, ensure the FTMON account can read /sys/class/thermal/*/temp.

Upstream and licence

nagios-plugins-linux by Davide Madrisan, GPL-3.0-or-later. FTMON does not redistribute the plugin.

Verified on 2026-07-14 with nagios-plugins-linux **v35** on Ubuntu 24.04 (Dell XPS 15): hottest-zone OK at -w 80 -c 90, warn/crit confirmed by lowering thresholds, unknown from -t 999.

Registry example

[check.temperature]
# Warn/crit Celsius for the hottest thermal zone (plugin default without -t).
# Pin a zone with "-t", "<n>" after checking: check_temperature --list
argv = [
  "/usr/lib/nagios/plugins/check_temperature",
  "-w", "80",
  "-c", "90",
]
protocol = "nagios"
timeout = "10s"

Monitor definition

schema = 1

[monitor]
name = "temperature"
description = "Hardware temperature via check_temperature (hottest thermal zone)"
version = 3
enabled = false
platforms = ["linux"]
interval = "60s"
source = "external"

[source_options]
check = "temperature"
entity = "thermal:max"

[[source_options.perfdata]]
label = "temp"
metric = "temp_celsius"
plugin_uom = "C"
unit = "celsius"
kind = "gauge"

[parameters]
warn_celsius = { value = 80, doc = "Warning threshold also configured as -w in checks.toml" }
crit_celsius = { value = 90, doc = "Critical threshold also configured as -c in checks.toml" }
rise_celsius_per_h = { value = 8, doc = "Sustained °C/hour climb over 2h before warning (cooling / load)" }
rise_confidence_min = { value = 0.8, doc = "Required fraction of rising temperature samples in the window" }

# External plugin configuration remains authoritative for alert state. These explicit
# parameters let the dashboard show the same operator-maintained thresholds.
[glance]
metric = "temp_celsius"
unit = "celsius"
aggregate = "max"
thresholds = [
  { label = "warn", parameter = "warn_celsius" },
  { label = "critical", parameter = "crit_celsius" },
]

[[derived]]
name = "temp_rate_cph"
expr = 'slope(temp_celsius, "2h") * 3600'

[[derived]]
name = "temp_rise_confidence"
expr = 'monot(temp_celsius, "2h")'

[[rule]]
id = "plugin-warning"
group = "thermal"
when = "plugin_state == 1"
severity = "warning"
confirm_cycles = 3
message = "{plugin_message}"

[[rule]]
id = "plugin-critical"
group = "thermal"
when = "plugin_state == 2"
severity = "critical"
confirm_cycles = 3
message = "{plugin_message}"

[[rule]]
id = "plugin-unknown"
group = "check-health"
when = "plugin_state == 3"
severity = "warning"
confirm_cycles = 2
message = "Temperature check is unknown: {plugin_message}"

[[rule]]
id = "temp-rising"
group = "thermal-rise"
when = "temp_rate_cph > rise_celsius_per_h and temp_rise_confidence >= rise_confidence_min"
severity = "warning"
confirm_cycles = 3
message = "Hottest thermal zone is climbing steadily ({temp_celsius} °C)"

[[trend]]
id = "temp-rise"
kind = "growth"
title = "Temperature rise trend"
value_metric = "temp_celsius"
value_unit = "celsius"
rate_metric = "temp_rate_cph"
rate_unit = "celsius/hour"
confidence_metric = "temp_rise_confidence"
confidence_threshold_param = "rise_confidence_min"
rate_threshold_params = ["rise_celsius_per_h"]