Cooling and Overclocking a Raspberry Pi 4 Safely: Measure, Then Tune

The Raspberry Pi 4 is far more powerful than earlier models, and it also produces more heat. Without a heatsink, a long compile, video encode or OpenCV job can push the SoC to its temperature limit, at which point the firmware reduces the clock speed. Your program keeps running, just slower, and many people never notice why.

This tutorial takes an engineering approach: first measure temperature and clock speed under a repeatable load, then add cooling, then overclock only if the cooling has headroom, and finally re-measure to prove the result is stable.

How Thermal Throttling Works

The firmware watches the SoC temperature continuously. At the soft limit of 80 °C it starts reducing the ARM clock to limit heat. If the temperature still rises to 85 °C, it throttles further. The vcgencmd get_throttled flags record whether this has happened since boot, so you can check after a test instead of watching the whole time.

CommandShowsExample output
vcgencmd measure_tempSoC temperaturetemp=61.3'C
vcgencmd measure_clock armCurrent ARM clock in Hzfrequency(48)=1800457088
vcgencmd get_throttledThrottling and power flagsthrottled=0x0
vcgencmd measure_volts coreCore voltagevolt=0.8600V

Step 1: Measure Under Load

Install stress-ng with sudo apt install -y stress-ng. The script below starts a four-core CPU load for a set time and records temperature, clock and throttle flags to a CSV file every two seconds. Run it once on the bare board, once with your cooling solution, and again after overclocking, and compare the files.

thermal_test.py - stress all cores and log temperature, clock and throttle flags
#!/usr/bin/env python3
# thermal_test.py - repeatable thermal test for a Raspberry Pi 4
# Usage: python3 thermal_test.py 600 baseline.csv   (seconds, output file)
import csv
import subprocess
import sys
import time

DURATION = int(sys.argv[1]) if len(sys.argv) > 1 else 600
OUTFILE = sys.argv[2] if len(sys.argv) > 2 else 'thermal.csv'

def vc(*args):
    """Return the value part of a vcgencmd reply, e.g. '61.3' from temp=61.3'C."""
    out = subprocess.check_output(['vcgencmd', *args], text=True).strip()
    return out.split('=')[1]

def sample():
    temp = float(vc('measure_temp').rstrip("'C"))
    mhz = int(vc('measure_clock', 'arm')) // 1_000_000
    flags = vc('get_throttled')
    return temp, mhz, flags

# Start stress-ng on all 4 cores in the background
load = subprocess.Popen(['stress-ng', '--cpu', '4', '--timeout', f'{DURATION}s'],
                        stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

start = time.time()
max_temp, min_mhz = 0.0, 10_000
with open(OUTFILE, 'w', newline='') as f:
    w = csv.writer(f)
    w.writerow(['seconds', 'temp_c', 'arm_mhz', 'throttled'])
    while load.poll() is None:              # until stress-ng exits
        t, mhz, flags = sample()
        elapsed = int(time.time() - start)
        w.writerow([elapsed, t, mhz, flags])
        max_temp, min_mhz = max(max_temp, t), min(min_mhz, mhz)
        print(f'{elapsed:4d}s  {t:5.1f} C  {mhz:5d} MHz  {flags}')
        time.sleep(2)

print(f'Peak temperature: {max_temp:.1f} C, lowest clock: {min_mhz} MHz')
print('Final flags:', vc('get_throttled'), '(0x0 means no throttling)')

If the lowest clock in the log is below the board's normal maximum, or bit 18 or 19 of the final flags is set (values like 0x40000 or 0x80000), the board throttled during the test.

Step 2: Add Cooling

OptionNoiseComment
Stick-on heatsinksSilentHelps short bursts; not enough for long full load in a closed case
Large passive aluminium caseSilentThe whole case acts as a heatsink; good for NAS and servers
Official Case Fan or a 30 mm 5 V fanLow to mediumStrong cooling; best when switched by temperature
Heatsink plus fan HATMediumHighest headroom for overclocking

A fan running all the time is noisy and wears out. Bookworm's firmware can switch it for you with the gpio-fan overlay. Drive the fan through an NPN transistor (for example a 2N2222 with a 1 k base resistor from GPIO14, fan negative to the collector, emitter to GND, and a 1N4148 diode across the fan) because a GPIO pin cannot supply fan current. Then add this line to /boot/firmware/config.txt:

# Fan on GPIO14 switches on at 60 C (value in millidegrees)
dtoverlay=gpio-fan,gpiopin=14,temp=60000

The same setting is available in raspi-config under Performance Options, Fan.

Step 3: Overclock in Small Steps

Overclocking is set in /boot/firmware/config.txt under the [all] section. A commonly used, moderate setting for a well-cooled Pi 4 is shown below. over_voltage raises the core voltage in steps of 0.025 V, which the higher clock usually needs to remain stable.

[all]
# Moderate Pi 4 overclock - only with active cooling
over_voltage=6
arm_freq=2000
# Optional GPU clock increase
gpu_freq=750
  • Back up config.txt first: sudo cp /boot/firmware/config.txt /boot/firmware/config.txt.bak.
  • Increase arm_freq in steps of 100 MHz and run the thermal test after each step.
  • If the Pi will not boot after a change, put the card in a PC and edit config.txt on the boot partition to undo it. The boot partition is FAT32, so Windows can open it directly.
  • Do not use force_turbo=1 together with over_voltage above 6. That combination sets a permanent bit in the SoC that records the board was over-volted, which can void the warranty.
  • Not every chip reaches the same clock. If a setting causes random crashes or SD card errors, step back down; stability matters more than benchmark numbers.

Common Mistakes

  • Judging stability by a quick boot. Always run at least ten minutes of full load before trusting a setting.
  • Forgetting the power supply. Overclocked boards draw more current; under-voltage flags (bit 0 or 16) mean the supply, not cooling, is the problem.
  • Connecting a fan straight to a GPIO pin. It will not spin properly and can damage the pin.
  • Covering the board in a closed plastic case with a fan but no air inlet.

Summary

Measure first with a repeatable stress test that logs temperature, clock and throttle flags. Add heatsinks or a temperature-controlled fan with dtoverlay=gpio-fan, then raise arm_freq with a matching over_voltage in small steps, re-running the same test each time. Keep the settings only if the final flags read 0x0 and the clock never drops under load.