fanctl is a small macOS fan-control CLI for Apple Silicon Macs.
It provides:
- read-only fan status
- fixed RPM control
- max fan mode
- return-to-Apple automatic mode
- temperature sensor discovery
- daemon-managed temperature-driven profiles
The project is intentionally boring: SwiftPM, no GUI, no menu bar, no cloud, no telemetry.
Status: personal prototype. It works on the author's Mac, but AppleSMC keys and write behavior can vary by model and macOS version. Read the safety notes before using write commands.
Most fan tools are GUI-first. fanctl is for people who want terminal control:
fanctl status
fanctl sensors
fanctl profile balanced
fanctl max
fanctl autoWrites are handled by a root LaunchDaemon (fanctld) so you approve installation once, then use fanctl without typing sudo for every command.
fanctl statusShows fan RPM, target RPM, hardware min/max, and current mode.
fanctl sensorsReads a curated list of AppleSMC temperature keys and prints sane values.
fanctl autoStops any active fanctl profile and returns fans to macOS automatic control.
fanctl set 3500Sets all fans to a validated absolute RPM. RPMs below detected min or above detected max are rejected.
fanctl maxSets all fans to their detected maximum RPM.
fanctl profile quiet
fanctl profile balanced
fanctl profile coolProfiles are daemon-managed curves, not one-shot fixed values. fanctld reads temperature every ~2 seconds, maps temperature to target RPM, and applies hysteresis/ramp limits.
Initial behavior:
| Profile | Start ramp | Midpoint | Max by | Idle floor |
|---|---|---|---|---|
| quiet | 62°C | 78°C | 95°C | fan min |
| balanced | 55°C | 72°C | 88°C | min + 15% |
| cool | 48°C | 65°C | 82°C | min + 25% |
Emergency rule: if the control temperature reaches 95°C, fans go to max.
Requirements:
- macOS 13+
- Apple Silicon Mac
- Xcode or Command Line Tools with Swift
Clone/build:
git clone <repo-url> fanctl
cd fanctl
swift build
.build/debug/fanctl-selftestInstall daemon and CLI:
sudo ./install_fanctld.shThis installs:
/usr/local/bin/fanctl/usr/local/libexec/fanctld/Library/LaunchDaemons/io.github.fanctl.fanctld.plist- socket:
/var/run/fanctld.sock - log:
/var/log/fanctld.log
Uninstall:
sudo ./uninstall_fanctld.shfanctl status
fanctl sensors
fanctl profile balanced
fanctl status
fanctl auto
fanctl set 3500
fanctl maxLogs:
tail -f /var/log/fanctld.logThis tool writes private AppleSMC fan keys. That is inherently unofficial.
Guardrails included:
- validates requested RPM against detected fan min/max
- verifies target RPM writes stick
autostops profile loop and returns control to macOS- temp profiles fail if no valid temperature sensors are found
- emergency max at 95°C
- hysteresis and ramp limiting to avoid noisy oscillation
Things still not production-grade:
- socket is local world-writable in the prototype
- no notarized helper / SMAppService install flow
- sensor key list is curated, not exhaustive
- third-party fan apps can fight this daemon
Quit other fan-control tools before testing.
Build:
swift buildSelftest:
.build/debug/fanctl-selftestXCTest exists under Tests/, but fanctl-selftest is the authoritative test path because XCTest was unreliable on the original development machine.
Debug daemon without installing:
rm -f /tmp/fanctld.sock
FANCTLD_SOCKET=/tmp/fanctld.sock .build/debug/fanctldIn another terminal:
FANCTLD_SOCKET=/tmp/fanctld.sock .build/debug/fanctl sensorsfanctl CLI
-> Unix socket /var/run/fanctld.sock
-> fanctld root LaunchDaemon
-> AppleSMC via IOKit
Main modules:
FanCtlCore: command parsing, daemon protocol, SMC formats, fan control, temp curvesFanCtlCLI: user CLIFanCtlDaemon: root daemon and socket serverFanCtlSelfTest: no-hardware fake-backend test executable
This project was informed by existing open-source AppleSMC/fan-control work, especially:
MIT. See LICENSE.