JoshuaDavis

nvidia-fan-control

Feb 18th, 2026
7,636
0
Never
2
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.91 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. """
  3. NVIDIA Aggressive Fan Control Daemon
  4. RTX Pro 6000 Blackwell - Gaming-style fan curve
  5. Runs headless, no X11 required.
  6. """
  7.  
  8. import pynvml
  9. import time
  10. import logging
  11. import sys
  12.  
  13. logging.basicConfig(
  14.     level=logging.INFO,
  15.     format='%(asctime)s %(levelname)s %(message)s',
  16.     handlers=[logging.StreamHandler(sys.stdout)]
  17. )
  18. log = logging.getLogger(__name__)
  19.  
  20. # --- Fan curve: (temp_C, fan_pct) ---
  21. # More aggressive than stock workstation defaults.
  22. # By 70C fans are already at 80%, by 80C they're maxed.
  23. FAN_CURVE = [
  24.     (0,  30),
  25.     (45, 40),
  26.     (55, 55),
  27.     (63, 70),
  28.     (70, 82),
  29.     (75, 90),
  30.     (80, 100),
  31. ]
  32.  
  33. POLL_INTERVAL = 3   # seconds between checks
  34. GPU_INDEX     = 0   # change if you have multiple GPUs
  35.  
  36.  
  37. def interpolate_fan(temp):
  38.     """Return target fan % for a given GPU temp using the curve above."""
  39.     if temp <= FAN_CURVE[0][0]:
  40.         return FAN_CURVE[0][1]
  41.     if temp >= FAN_CURVE[-1][0]:
  42.         return FAN_CURVE[-1][1]
  43.     for i in range(1, len(FAN_CURVE)):
  44.         t1, f1 = FAN_CURVE[i - 1]
  45.         t2, f2 = FAN_CURVE[i]
  46.         if t1 <= temp <= t2:
  47.             ratio = (temp - t1) / (t2 - t1)
  48.             return int(f1 + ratio * (f2 - f1))
  49.     return 100
  50.  
  51.  
  52. def set_fans(handle, num_fans, pct):
  53.     pct = max(0, min(100, pct))
  54.     for fan_idx in range(num_fans):
  55.         pynvml.nvmlDeviceSetFanSpeed_v2(handle, fan_idx, pct)
  56.  
  57.  
  58. def main():
  59.     pynvml.nvmlInit()
  60.     driver = pynvml.nvmlSystemGetDriverVersion()
  61.     log.info(f"NVIDIA driver: {driver}")
  62.  
  63.     handle   = pynvml.nvmlDeviceGetHandleByIndex(GPU_INDEX)
  64.     name     = pynvml.nvmlDeviceGetName(handle)
  65.     num_fans = pynvml.nvmlDeviceGetNumFans(handle)
  66.     log.info(f"Controlling: {name}  ({num_fans} fans)")
  67.     log.info("Fan curve (temp->fan%): " +
  68.              "  ".join(f"{t}C->{f}%" for t, f in FAN_CURVE))
  69.  
  70.     # Take manual control of the fan
  71.     for fan_idx in range(num_fans):
  72.         pynvml.nvmlDeviceSetFanControlPolicy(
  73.             handle, fan_idx,
  74.             pynvml.NVML_FAN_POLICY_MANUAL
  75.         )
  76.     log.info("Fan control set to MANUAL")
  77.  
  78.     last_pct = -1
  79.     try:
  80.         while True:
  81.             temp    = pynvml.nvmlDeviceGetTemperature(
  82.                 handle, pynvml.NVML_TEMPERATURE_GPU)
  83.             target  = interpolate_fan(temp)
  84.  
  85.             if target != last_pct:
  86.                 set_fans(handle, num_fans, target)
  87.                 log.info(f"GPU {temp}C -> fans {target}%")
  88.                 last_pct = target
  89.  
  90.             time.sleep(POLL_INTERVAL)
  91.  
  92.     except KeyboardInterrupt:
  93.         log.info("Interrupted - restoring automatic fan control")
  94.     finally:
  95.         # Hand control back to the driver on exit
  96.         for fan_idx in range(num_fans):
  97.             pynvml.nvmlDeviceSetFanControlPolicy(
  98.                 handle, fan_idx,
  99.                 pynvml.NVML_FAN_POLICY_TEMPERATURE_CONTINOUS_SW
  100.             )
  101.         pynvml.nvmlShutdown()
  102.         log.info("Fan control restored to automatic")
  103.  
  104.  
  105. if __name__ == "__main__":
  106.     main()
  107.  
Advertisement