UniFi Dream Router · fan noise fix
Your UniFi Dream Router spins its fan at about 2,000 RPM even when it is barely warm. One small settings file tells Ubiquiti's own fan controller to relax. No scripts, no fighting, and it can be undone with one command.
The whole fix in about a minute. Press play, or click a bar to jump to a scene. Every command here is in the steps below with a copy button.
A program on the router called uhwd runs the fan. It looks at the CPU temperature and the internal SSD temperature, then picks a speed from a built-in table. On the UDR that table asks for 20–30% fan whenever the CPU is under 60 °C. A normal UDR idles around 57–61 °C, so the fan sits near 30%, which the fan chip turns into roughly 2,000 RPM.
Many guides tell you to write your own fan script. uhwd keeps running and writes its own value back. Your script lowers the fan, uhwd raises it, and the fan surges up and down every few seconds. That is often worse than the original noise.
uhwd reads an optional file, /etc/ustd/fan.conf. If it exists, its table replaces the built-in one. You change the curve inside Ubiquiti's controller instead of fighting it.
This file is not documented by Ubiquiti. It was tested on one UniFi Dream Router running UniFi OS 5.1.33 in October 2026. You do this at your own risk. Undoing it takes one command (step 8). The new curve still ramps to full speed when the router gets hot, so the hardware stays protected.
You need:
ssh built in.192.168.1.1; replace it with yours.Open Terminal (macOS/Linux) or PowerShell (Windows) and run this. Type yes if it asks about a fingerprint, then enter the SSH password. Nothing shows while you type the password. That is normal.
You are in when the line starts with root@.
This prints the CPU temperature, the fan setting (pwm, 0–255) and the real fan speed three times.
for i in 1 2 3; do echo "cpu=$(( $(cat /sys/class/thermal/thermal_zone0/temp) / 1000 ))C pwm=$(cat /sys/class/hwmon/hwmon0/pwm1) rpm=$(cat /sys/class/hwmon/hwmon0/fan1_input)"; sleep 5; done
Typical result: cpu=58C pwm=73 rpm=2050. Write your numbers down so you can compare later.
If you tried a fan script before, switch it off first, or it will fight the new setup. This shows any service with "fan" in its name.
systemctl list-units --all | grep -i fan
No output is good. If you see your own script (for example fancontrol.service), run systemctl disable --now fancontrol.
Copy the whole block and paste it in one go. It writes the file and stops at the line EOF.
cat > /etc/ustd/fan.conf <<'EOF'
{
"2": ["cpu", -40, 70, 16, 16],
"3": ["cpu", 70, 80, 39, 39],
"4": ["cpu", 80, 90, 70, 70],
"5": ["cpu", 90, 200, 100, 100],
"12": ["flash", -40, 60, 16, 16],
"13": ["flash", 60, 65, 39, 39],
"14": ["flash", 65, 70, 70, 70],
"15": ["flash", 70, 200, 100, 100],
"PID": {"cpu": [82, -0.1, -0.1, 0.0, false, 2, 10],
"flash": [62, -0.1, -0.1, 0.0, false, 2, 10]},
"targets": {"1": ["all"]},
"standby": 10,
"calc_type": "table"
}
EOFpython3 -m json.tool /etc/ustd/fan.conf > /dev/null && echo "fan.conf OK"
You should see fan.conf OK. If you get an error instead, run step 4 again.
Restarting the status service makes it read the file and pass it on to the fan controller. Restarting uhwd alone does not load it.
systemctl restart usdbd
uhwd stops for a second and starts again by itself. That is expected. Your internet connection is not affected.
Wait about 10 seconds, then run the step 2 command again.
for i in 1 2 3; do echo "cpu=$(( $(cat /sys/class/thermal/thermal_zone0/temp) / 1000 ))C pwm=$(cat /sys/class/hwmon/hwmon0/pwm1) rpm=$(cat /sys/class/hwmon/hwmon0/fan1_input)"; sleep 5; done
Expect something like cpu=59C pwm=36 rpm=630. The rpm value comes from the fan itself, so a number above zero proves it is still spinning. A stopped fan reads 0.
rm /etc/ustd/fan.conf && systemctl restart usdbd
The fan goes back to about 2,000 RPM within seconds.
Each line reads [sensor, from °C, to °C, fan % at start, fan % at end]. cpu is the processor; flash is the internal SSD. The router uses whichever sensor asks for the higher speed.
| CPU temperature | Factory | This recipe | |
|---|---|---|---|
| under 60 °C | 20–30 % | 16 % | everyday |
| 60–70 °C | 30–36 % | 16 % | warm day |
| 70–80 °C | 36–44 % | 39 % | |
| 80–90 °C | 44–50 % | 70 % | ramps sooner |
| 90 °C and up | 50–100 % | 100 % | ramps sooner |
The SSD lines work the same way: 16% under 60 °C, then 39%, 70% and 100% at 60, 65 and 70 °C.
The fan chip only has a few real speeds. A smooth ramp keeps landing between two of them, and tiny temperature wobbles then flip the fan between speeds. You hear that as pulsing. Flat steps keep it steady.
The table has no built-in delay at the edges. If your CPU sits right at 70 °C, for example in a hot cupboard in summer, the fan may switch between quiet and 39% at that line. If that happens, raise or lower the 70 in both cpu lines so it sits away from your normal temperature, then repeat steps 5 and 6.
Firmware updates may remove the file. If they do, nothing breaks. The fan simply goes back to the factory noise. To check:
ls -l /etc/ustd/fan.conf
If it says "No such file", repeat steps 4 to 7. That takes about a minute.
This block saves a small script called quiet-fan.sh on your router and runs it. The script does steps 2 to 7 for you and prints the fan speed before and after. Read it first if you like. It is short, and everything it does is written in plain comments at the top.
/root before writing.sh /root/quiet-fan.sh undo brings back the factory curve.ssh [email protected]pwm=36 and roughly 600–700 rpm.Option A, download: fetch the script from this site, check it, then run it.
curl -fsSL https://guide.viavi.dk/unifi-dream-router-quiet-fan/quiet-fan.sh -o /root/quiet-fan.sh echo "0048b0e0e41633662c0ad156a385257e67807c06abe25f903209d74ba44d3def /root/quiet-fan.sh" | sha256sum -c - && sh /root/quiet-fan.sh install
The second line only runs the script if the file matches, and prints /root/quiet-fan.sh: OK first. You can also read the script here.
Option B, paste: no download needed, the whole script is in this block.
cat > /root/quiet-fan.sh <<'QUIETFAN'
#!/bin/sh
# quiet-fan.sh - quiet the fan on a UniFi Dream Router (UDR)
#
# Gives Ubiquiti's own fan controller (uhwd) a calmer table via
# /etc/ustd/fan.conf. Nothing else on the router is changed.
#
# sh quiet-fan.sh install apply the quiet table (backs up any existing one)
# sh quiet-fan.sh status show temperature and fan speed, change nothing
# sh quiet-fan.sh undo remove the table, back to the factory curve
#
# Tested on one UDR, UniFi OS 5.1.33, October 2026. Use at your own risk.
CONF=/etc/ustd/fan.conf
H=/sys/class/hwmon/hwmon0
CPU=/sys/class/thermal/thermal_zone0/temp
measure() {
for i in 1 2 3; do
echo " cpu=$(( $(cat $CPU) / 1000 ))C pwm=$(cat $H/pwm1) rpm=$(cat $H/fan1_input)"
[ "$i" -lt 3 ] && sleep 5
done
}
preflight() {
if [ "$(id -u)" != 0 ]; then
echo "Please run this as root (log in with: ssh root@<router-ip>)."; exit 1
fi
model=$(ubnt-device-info model_short 2>/dev/null)
if [ "$model" != "UDR" ]; then
echo "This router reports model '${model:-unknown}', not 'UDR'."
echo "The fan table is only tested on the UniFi Dream Router. Nothing was changed."
exit 1
fi
if [ ! -d /etc/ustd ] || [ ! -r $H/pwm1 ] || [ ! -r $H/fan1_input ] || [ ! -r $CPU ]; then
echo "Could not find Ubiquiti's fan controller or the fan sensors. Nothing was changed."
exit 1
fi
}
load() {
# usdbd reads fan.conf and hands it to uhwd; uhwd restarts itself for a second.
systemctl restart usdbd
echo " Waiting 10 seconds for the fan to settle..."
sleep 10
}
case "${1:-}" in
install)
preflight
others=$(systemctl list-units --all --plain --no-legend 2>/dev/null | awk '{print $1}' | grep -i fan)
if [ -n "$others" ]; then
echo "Found other fan services that would fight the new table:"
echo "$others" | sed 's/^/ /'
echo "Stop them first, e.g.: systemctl disable --now <name>. Nothing was changed."
exit 1
fi
echo "Before:"; measure
if [ -f $CONF ]; then
backup=/root/fan.conf.backup-$(date +%Y%m%d-%H%M%S)
cp $CONF "$backup" && echo " Saved your existing table to $backup"
fi
cat > $CONF.new <<'CONF'
{
"2": ["cpu", -40, 70, 16, 16],
"3": ["cpu", 70, 80, 39, 39],
"4": ["cpu", 80, 90, 70, 70],
"5": ["cpu", 90, 200, 100, 100],
"12": ["flash", -40, 60, 16, 16],
"13": ["flash", 60, 65, 39, 39],
"14": ["flash", 65, 70, 70, 70],
"15": ["flash", 70, 200, 100, 100],
"PID": {"cpu": [82, -0.1, -0.1, 0.0, false, 2, 10],
"flash": [62, -0.1, -0.1, 0.0, false, 2, 10]},
"targets": {"1": ["all"]},
"standby": 10,
"calc_type": "table"
}
CONF
if ! python3 -m json.tool $CONF.new > /dev/null 2>&1; then
rm -f $CONF.new; echo "The new table did not validate. Nothing was changed."; exit 1
fi
mv $CONF.new $CONF
load
echo "After:"; measure
echo
echo "Done. Expect pwm=36 and roughly 600-700 rpm at normal temperatures."
echo "rpm above 0 means the fan is spinning. To undo: sh $0 undo"
;;
status)
if [ -f $CONF ]; then echo "Quiet table: installed ($CONF)"; else echo "Quiet table: not installed (factory curve)"; fi
measure
;;
undo)
preflight
if [ ! -f $CONF ]; then echo "No quiet table installed. Nothing to undo."; exit 0; fi
mv $CONF /root/fan.conf.removed-$(date +%Y%m%d-%H%M%S)
load
echo "Back to the factory curve:"; measure
;;
*)
echo "Usage: sh $0 install | status | undo"; exit 1
;;
esac
QUIETFAN
sh /root/quiet-fan.sh installLater, check or undo it with:
sh /root/quiet-fan.sh status
sh /root/quiet-fan.sh undo
After a firmware update, run sh /root/quiet-fan.sh status. If it says "not installed", run sh /root/quiet-fan.sh install. If the script itself is gone too, paste the block above again.
Ubiquiti's fan controller (uhwd) runs the fan at 20-30% whenever the CPU is under 60 °C. A UDR idles around 57-61 °C, so the fan sits near 30%, which is roughly 2,000 RPM.
The quiet table keeps 16% fan up to 70 °C CPU, then ramps to 39%, 70% and 100% at 70, 80 and 90 °C, and also follows the internal SSD. Ubiquiti's own table only reaches 100% at 110 °C. In our test the CPU settled at 63-66 °C with the fan at about 650 RPM.
uhwd keeps writing its own fan value. A script that lowers the fan fights it, and the fan surges between about 650 and 2,050 RPM every few seconds. Changing uhwd's own table avoids that.
Not guaranteed. If an update removes /etc/ustd/fan.conf, the fan simply returns to the factory curve. Run sh /root/quiet-fan.sh status after updates and install again if needed.
It was only tested on the original UniFi Dream Router (model UDR) with UniFi OS 5.1.33. Other models use different fan tables, and the script refuses to run on them.
Run rm /etc/ustd/fan.conf && systemctl restart usdbd, or sh /root/quiet-fan.sh undo. The factory curve is back within seconds.