Skip to main content

Fan control daemon for TrueNAS SCALE with Netdata metrics

Project description

truefan

Fan control daemon for TrueNAS SCALE systems on Supermicro X11 boards. Takes over fan control from the BMC so you can tune noise vs. cooling to your environment. Reads temperatures from IPMI, SMART, NVMe, and lm-sensors, then sets fan duty cycles via IPMI raw commands. Metrics go to Netdata via statsd.

Features

  • Auto-detection of sensors and fans — classifies by type (cpu, drive, nvme, ambient, other) and applies per-class interpolation curves.
  • Self-calibrating — learns each fan's setpoint table by ramping duty down and recording RPMs. Can be recalibrated as fans age or collect dust.
  • Failsafe — fans go to 100% on crash, total sensor class failure, or stalled fan. The watchdog parent restarts the daemon automatically.
  • Config validation — startup, reload, and the check command validate config syntax, values, and hardware match before touching any fans.
  • Syslog logging — the daemon logs to syslog; truefan logs wraps journalctl for easy access.
  • Netdata metrics — per-sensor temperature and thermal load, per-zone duty, per-fan target RPM, daemon uptime, and restart count via statsd. Optional alert configs included.

Requirements

  • Python 3.11+
  • ipmitool
  • smartctl (smartmontools) — for SATA/SAS drive temps
  • nvme-cli — for NVMe temps
  • lm-sensors — for kernel-exposed sensors
  • Supermicro X11 motherboard with IPMI

Install

TrueNAS SCALE doesn't ship ensurepip, so create the venv without it and bootstrap pip manually:

python3 -m venv --without-pip /mnt/pool1/venvs/truefan
source /mnt/pool1/venvs/truefan/bin/activate
curl -sS https://bootstrap.pypa.io/get-pip.py | python3
pip install truefan

Put the venv on a pool — the boot drive is wiped on OS updates.

Quick start

# Detect sensors, calibrate fans, write config
# (ramps fans up and down for a few minutes)
sudo truefan init

# Start the daemon (daemonizes and returns immediately)
sudo truefan start

# Check if it's running
truefan status

# Show detected sensors and current readings
truefan sensors

# Follow daemon logs
truefan logs -f

# Reload config without restarting
sudo truefan reload

# Re-calibrate after cleaning or replacing fans
sudo truefan recalibrate

# Stop the daemon
sudo truefan stop

To get Netdata dashboards and alerts, install the bundled configs:

sudo ./netdata/install.sh standalone

In a streaming setup, run with child on the daemon box and parent on the central Netdata instance. See ./netdata/install.sh --help for details.

Configuration

truefan init generates a truefan.toml with sensible defaults. Example:

poll_interval_seconds = 15
spindown_window_seconds = 180

[curves.drive]
temp_low = 30
temp_high = 45
duty_low = 25
duty_high = 100
fan_zones = ["peripheral"]

# Per-sensor overrides for components that run hotter than their class
[curves.sensor.lmsensors_mlx5_pci_0200_sensor0]
temp_low = 60
temp_high = 95

# Learned via calibration — duty % = expected RPM
[fans.FAN1]
zone = "cpu"

[fans.FAN1.setpoints]
25 = 320
30 = 450
40 = 620
50 = 780
100 = 1500

Use --config PATH with any command to specify an alternate config location.

Running on boot

TrueNAS SCALE's Init/Shutdown Scripts (under System > Advanced) run commands at boot and shutdown.

Add a script (Type: Command, When: Post Init):

/mnt/pool1/venvs/truefan/bin/truefan start

No tmux or nohup needed — start forks into the background on its own. Use truefan logs -f to follow output and truefan status to check if it's running.

How it works

Each sensor class has a temperature-to-duty curve. Between temp_low and temp_high, duty is linearly interpolated; hardware-reported thermal limits override temp_high when available. The hottest sensor in each fan zone sets the duty. A spindown window prevents rapid cycling.

If a fan stalls, the zone goes to 100% and the lowest setpoint is removed so the minimum duty rises going forward.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

truefan-1.0.0.tar.gz (70.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

truefan-1.0.0-py3-none-any.whl (37.6 kB view details)

Uploaded Python 3

File details

Details for the file truefan-1.0.0.tar.gz.

File metadata

  • Download URL: truefan-1.0.0.tar.gz
  • Upload date:
  • Size: 70.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for truefan-1.0.0.tar.gz
Algorithm Hash digest
SHA256 2ea77f8552d43f9da0f448292bfc5f7f378775cce2bb98a50a4188e5833d7526
MD5 da683658ba872335d5ab8e70569e36a1
BLAKE2b-256 dbc9b73505df07e19b03c845269ae2fd3ef509f49ac50cc5ae6df6044ff7540f

See more details on using hashes here.

Provenance

The following attestation bundles were made for truefan-1.0.0.tar.gz:

Publisher: ci.yml on zvea/truefan

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file truefan-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: truefan-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 37.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for truefan-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9c43dfa3a7162657c22a0365260f4c68adf671fb7f87fa3b51748350bf13a6f5
MD5 876865288294b25df62604364ba69ab0
BLAKE2b-256 8a91623d51069ab6ff4425d79f5ecc225f28c001030bcf9d351b78191fb4d0ee

See more details on using hashes here.

Provenance

The following attestation bundles were made for truefan-1.0.0-py3-none-any.whl:

Publisher: ci.yml on zvea/truefan

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page