The Problem

The tiny 2U CPU cooler fans in my server are extremely loud even though the temps are low. None of the default fan curves quiet the fans to an acceptable level. The server uses a Supermicro motherboard, and my specific model lacks any sort of complex fan management from the stock tools.

The Solution

There is a premade solution appropriately called fancontrol. Try this first, it might work on your model of motherboard. Unfortunately this did not work for me as it did not recognize the fans and sensors properly.

Instead, I am going to write a script to automatically ramp the fan speed based on temperature. To do this we need 2 things:

  • The current temperature of the CPU(s)

  • A way to set the fan speed

To knock out the first item, we want to read out the temperatures we want to monitor. Before you start, you will likely need to load the kernel module for your temperature sensors. You can do this with sensors-detect from the lm-sensors package. Make sure to add this to /etc/modules and load it with modprobe. Once we have done this, we can read temps. My server is running Proxmox which is based on Debian, so these temperatures can be read with cat /sys/class/thermal/thermal_zoneX/temp where X is a number starting from 0. You will probably only have 1 or 2 of these zones. First step done!

If you’ve used a Supermicro motherboard in your server(s), you’re probably familiar with IPMI. It’s a tool we can use to remotely (or locally) manage the motherboard’s hardware from inside our operating system. For reference, the specific motherboard I have is a Supermicro X9DRH-iTF. If you don’t already have it, install ipmitools so we can execute commands.

This tutorial assumes you are executing these commands locally on the host OS of the server.

So first we want to turn off all fan control and keep the fans at maximum speed. To do this we need to use IPMI raw commands. These will likely differ based on your specific motherboard, so do your research. Fortunately a user on reddit documented the commands required for my model.

The command I used to set the fan profile to full is raw 0x30 0x45 0x01 0x02 This ensures that the in-built fan control doesn’t try to override our custom ones.

Then you need to find a command that changes the speed of your CPU fans. For me, this is ipmitool raw 0x30 0x91 0x5A 0x3 0x10 0xFF where 0xFF is the speed of the fan in hex, between 0 and 255 inclusive.

Now we have all the tools we need to automatically control our fan speed. We want to get the max temperature from all zones and convert this to a value between 0 and 1 0, being the lowest target temp, and 1 being the highest. We then want to scale our fan speed between two values based on this number. To do this, I have used python. I will not explain the script in detail but it is pasted at the bottom of this article.

Once I had my script made up, I set it to run on system startup and voila, quiet fans!

The script

import os
import time

temp_locs = ["/sys/class/thermal/thermal_zone0/temp","/sys/class/thermal/thermal_zone1/temp"]

# paired with fan speed (0-255) and temp (degrees C)
f_low_speed = 40
f_low_temp = 35

f_high_speed = 255
f_high_temp = 80

temps = []
temp = 100
f_speed = 255


while True:
	temps.clear()
	for loc in temp_locs:
		with open(loc, "r") as file:
			temps.append(int(file.read()) // 1000)

	temp = max(temps)

	#find what speed the fan should be
	if temp < f_low_temp:
		f_speed = f_low_speed
	elif temp > f_high_temp:
		f_speed = f_high_speed
	else:
		f_speed_f = f_low_speed + ((temp - f_low_temp)/(f_high_temp - f_low_temp)*(f_high_speed - f_low_speed))
		f_speed = int(f_speed_f)

	print(hex(f_speed))
	print(temp)

	os.system('ipmitool raw 0x30 0x91 0x5A 0x3 0x10 ' + hex(f_speed))
	time.sleep(5)