Skip to main content

breezed

Curve-based fan controller for Dell PowerEdge servers via iDRAC/IPMI. Python 3.13 · uv · ruff · ty.

Source: github.com/martinmiglio/breezed

breezed is a curve-based fan controller for Dell PowerEdge servers, driving iDRAC/IPMI with a multi-point temperature curve and linear interpolation between points so homelab racks stay quiet at idle and cool under load. Above the top curve point it hands control back to iDRAC's own automatic thermal management — the safety net is always one degree away. Logs are JSON on stdout; a Prometheus-format metrics socket is opt-in; adapters (IPMI transport) and speed policies (curve today, PID later) sit behind small Protocols, so either can be swapped without touching the core.

Install

From PyPI:

pip install breezed
# or
uv tool install breezed

To deploy as a systemd service (the breezed binary needs privileges to talk to iDRAC; daemon install stages the unit and re-execs under sudo once), run one command and enter your password once:

breezed daemon install

daemon install stages the unit/env/config, then re-executes itself via sudo to perform the fixed privileged steps (create the service user, install the runtime under /opt, write the systemd unit, enable and start the service). Pass --dry-run to print the steps without escalating. The user-level uv only invokes breezed from the checkout; it is not copied into the system installation and is not a service dependency.

CLI examples

breezed run --config /etc/breezed/breezed.toml --metrics-port 9762 -v  # foreground daemon
breezed set 20            # one-shot 20% duty cycle
breezed auto              # one-shot back to iDRAC automatic control
breezed status --config /etc/breezed/breezed.toml   # snapshot: temps, RPMs, curve target
breezed validate breezed.toml --probe               # check config; probe reads live temp

The daemon subcommands manage the systemd deployment:

breezed daemon install         # one command, one sudo prompt
breezed daemon install --dry-run      # print the privileged steps without escalating
breezed daemon status                 # report unit state and binary version
breezed daemon uninstall              # one command, one sudo prompt; keeps env/config

Exit codes: 0 ok, 1 runtime error (e.g. IPMI failure), 2 usage/config error. Machine-readable output is always JSON on stdout; errors go to stderr as text.

Deploy (systemd)

systemd is the only deployment path — no containers. daemon install stages breezed.service, breezed.env, and breezed.toml into a private temp directory, prints the privileged steps, then re-executes itself once via sudo to perform them. uv installs and manages the runtime directly under /opt:

breezed daemon install
# one sudo password prompt; then:
sudoedit /etc/breezed.env
journalctl -u breezed -f

daemon install --dry-run prints the same steps without escalating, for review. The sudo re-exec runs only a fixed, code-reviewed sequence — no free-form commands, no shell — using the absolute path of the running binary, so it is immune to sudo's secure_path.

The runtime step sets UV_TOOL_DIR=/opt/breezed, UV_TOOL_BIN_DIR=/usr/local/bin, and UV_PYTHON_INSTALL_DIR=/opt/breezed-python, then installs the current checkout with --reinstall. The sequence creates the non-login breezed system user and installs files with explicit ownership and permissions:

  • /etc/systemd/system/breezed.service: root:root, mode 0644
  • /etc/breezed/breezed.toml: invoking user and group, mode 0664
  • /etc/breezed.env: root:breezed, mode 0640

The config and environment install steps run on first installation only and are skipped when those files already exist, so upgrades never overwrite tuned config or secrets.

Upgrading: update the checkout and run breezed daemon install again. The uv tool install --reinstall step replaces the runtime in /opt; the config and environment steps are skipped because those files already exist. breezed daemon uninstall mirrors install: one sudo prompt to disable and remove the service and runtime, while /etc/breezed/ and /etc/breezed.env remain.

The metrics port is baked into the unit's ExecStart deliberately (netdata scrapes it). To run without metrics, edit the unit to drop --metrics-port 9762 and systemctl daemon-reload && systemctl restart breezed; note your edit will be overwritten by the next daemon install.

Config example

A fully commented example ships inside the package: see breezed.toml.example in the breezed templates directory (also copied to /etc/breezed/breezed.toml on first install, never overwritten after that).

Secrets never go in the config file: IDRAC_HOST, IDRAC_USER, and IDRAC_PASSWORD come from the environment (/etc/breezed.env); host and user may also live in the config, but environment variables win. The config hot-reloads when its mtime changes; an invalid reload keeps the last good config.

Metrics (netdata)

With the default unit, breezed serves Prometheus text format on 127.0.0.1:9762. Scrape URL for netdata's prometheus collector: http://127.0.0.1:9762/metrics. Metric names: breezed_temp_c, breezed_fan_percent, breezed_mode, breezed_ipmi_errors_total, breezed_polls_total.

Safety

Manually controlling server fans means you are the thermal management system: a bad curve can cook components or leave a server roaring at 100%. breezed mitigates this by deferring to iDRAC automatic control above the curve top and forcing AUTO again after repeated sensor-read failures — but you chose the curve points, so choose them carefully. If anything looks wrong, breezed auto hands control back to the iDRAC instantly.

License

MIT

Metadata

Release files for breezed 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for breezed 0.2.0
File Size Uploaded
breezed-0.2.0.tar.gz 48.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for breezed 0.2.0
File Interpreter ABI Platform
breezed-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 77.7 kB

Release files / breezed-0.2.0.tar.gz

Download URL breezed-0.2.0.tar.gz
Size 48.0 kB
Tags Source
SHA-256 checksum
How to use checksums
bf6b6fd838bddbce03b1baa1ae3d5b09ff8ecb960447f724b4b2ae1fe6adac43
BLAKE2b-256 checksum
How to use checksums
0d19c2c66b52a82117aa68f9dbd3cdc05e239db07709b92cc8f7dc9ea5a02145
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / breezed-0.2.0-py3-none-any.whl

Download URL breezed-0.2.0-py3-none-any.whl
Size 29.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6768dc297945ed5de4ed011e7ea1fb34ded14f4937f3d7b4879a107d7bea9b4c
BLAKE2b-256 checksum
How to use checksums
4d08af409719036ed0ba0f7bc1ba73593d74147e0f20e86c54c250a4ac0353b5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page