2U Supermicro servers are my go-to. These are much quieter than 1U servers, but the fans spin at 8800RPM. The IPMI fan modes available are Full (9000RPM), Heavy IO (6000RPM), and Optimal (supposed to auto-adjust). Unfortunately, setting the Fan Mode to Optimal seems to have a floor speed of 4500RPM, which is too loud. Even though my servers are in the garage, I sometimes work on projects there and can’t have it that noisy!
In the past, I’ve replaced Supermicro fans with low RPM Antec or Noctua fans, but since I moved my homelab to the garage I don’t need it to be that quiet.
Here’s my script, which tries to control the fan speed such that the noise is below that of a jet engine and the CPU temperature is below 50C. To avoid hunting, I set a minimum and maximum CPU range between 40-50C. The logic is simple:
- Set FAN speed mode to Full (this allows manual control of the fans).
- If the CPU is above 50C, bump the speed up by 10%
- If the CPU is below 40C, drop fan speed by 1%
- Repeat steps 2 and 3 every minute
⚠️ WARNING 1: Using this script could overheat and damage your CPU and other components; your server may release the magic smoke, burn through crystals like you’re traveling at Warp 10, or catch fire due to using this script. I just wrote it yesterday, so there may be bugs. I advise you not to use this script. If you go against my advice, keep a close eye on your temperatures.
⚠️ WARNING 2: This script was designed for a Supermicro X10 Motherboard. Yours may have a different fan configuration, in which case you will want to add or change the ipmitool commands.

# apt install python3
# apt install ipmitools
# vim /usr/local/bin/fan_control.py
#!/usr/bin/env python3
import os
import subprocess
import time
import syslog
import re
# Set your desired temperature range and minimum fan speed
MIN_TEMP = 40
MAX_TEMP = 50
MIN_FAN_SPEED = 5 # Sets an initial fan speed of 5%
current_fan_speed = MIN_FAN_SPEED
# IPMI tool command to set the fan control mode to manual (Full)
os.system("ipmitool raw 0x30 0x45 0x01 0x01")
time.sleep(2)
# Get the current CPU temperature
def get_cpu_temperature():
temp_output = subprocess.check_output("ipmitool sdr type temperature", shell=True).decode()
cpu_temp_lines = [line for line in temp_output.split("\n") if "CPU" in line and "degrees" in line]
if cpu_temp_lines:
cpu_temps = [int(re.search(r'\d+(?= degrees)', line).group()) for line in cpu_temp_lines if re.search(r'\d+(?= degrees)', line)]
avg_cpu_temp = sum(cpu_temps) // len(cpu_temps)
return avg_cpu_temp
else:
print("Failed to retrieve CPU temperature.")
return None
# Set the fan speed
def set_fan_speed(speed):
global current_fan_speed
current_fan_speed = speed
# Convert the speed percentage to a hex value
hex_speed = format(speed * 255 // 100, "02x")
# Set the fan speed for all 4 zones
os.system(f"ipmitool raw 0x30 0x70 0x66 0x01 0x00 0x{hex_speed}")
time.sleep(2)
os.system(f"ipmitool raw 0x30 0x70 0x66 0x01 0x01 0x{hex_speed}")
time.sleep(2)
# Log the fan speed change to syslog
syslog.syslog(syslog.LOG_INFO, f"Fan speed adjusted to {speed}%")
# Print the fan speed change to console
print(f"Fan speed adjusted to {speed}% - {hex_speed}")
# Set initial minimum fan speed
set_fan_speed(MIN_FAN_SPEED)
while True:
cpu_temp = get_cpu_temperature()
# Print the current CPU temperature to console
print(f"Current CPU temperature: {cpu_temp}°C")
if cpu_temp > MAX_TEMP and current_fan_speed < 100:
# Increase the fan speed by 10% to cool down the CPU
new_fan_speed = min(current_fan_speed + 10, 100)
set_fan_speed(new_fan_speed)
elif cpu_temp < MIN_TEMP and current_fan_speed > MIN_FAN_SPEED:
# Decrease the fan speed by 1% if the temperature is below the minimum threshold
new_fan_speed = max(current_fan_speed - 1, MIN_FAN_SPEED)
set_fan_speed(new_fan_speed)
# Wait for 60 seconds before checking the temperature again
time.sleep(60)
# vim /etc/systemd/system/fan_control.service :
[Unit]
Description=Fan Controller Service
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/bin/python3 /usr/local/bin/fan_control.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
Set file permissions:
# chmod 755 /usr/local/bin/fan_control.py
# systemctl daemon-reload
# systemctl enable fan_control.service
# systemctl start fan_control.service
# systemctl status fan_control.service
Bugs: so, I’ve come across one bug where the script won’t spin down both fan zones, but killing the script and running it a second or third time seems to work. I suspect this is from sending ipmi commands too fast, so I’ve added sleep delays, but because it’s an intermittent issue who knows if that fixed it.
Improvement ideas: It would be great to add temperature monitoring for the other components (HDD, etc.). But in my observation, if I can keep the CPU cool the rest of the components are okay.
To watch the status, check IPMI or tail syslog:


# tail -F /var/log/syslog |grep -E ‘speed|temp’
Fan speed adjusted to 4% - 0a
Current CPU temperature: 38°C
Fan speed adjusted to 3% - 07
Current CPU temperature: 39°C
Fan speed adjusted to 2% - 05
Current CPU temperature: 39°C
Fan speed adjusted to 1% - 02
...
Current CPU temperature: 48°C
Current CPU temperature: 50°C
Current CPU temperature: 52°C
Fan speed adjusted to 11%
Current CPU temperature: 51°C
Current CPU temperature: 48°C
Current CPU temperature: 46°C
That was set up last night.
When I woke up this morning, the fans were running at a pleasant hum of 1800 RPM.
He who blesses his friend with a loud voice early in the morning, It will be counted as a curse to him. – Proverbs 27:14 LSB