A small white router on a shelf in a dim, calm living room, with a cat asleep on the sofa behind it. UniFi Dream Router · fan noise fix

How to make your UniFi Dream Router (UDR) fan quiet

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.

2,050 RPM
Before · factory curve at 58 °C
630 RPM
After · same temperature, new curve

Watch it first

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.

root@UDR: ~
Scene 1 · Log in to the router
you@laptop:~$ ssh [email protected] [email protected]'s password: Welcome to UniFi Dream Router! root@UDR:~#

Why it is loud, and why scripts make it worse

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.

The trick

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.

Before you start

Read this once

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:

The recipe

  1. Log in to the router

    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@.

  2. Measure the fan as it is now

    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.

  3. Check that nothing else controls the fan

    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.

  4. Create the new fan table

    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"
    }
    EOF
  5. Check the file for typos

    python3 -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.

  6. Load the new table

    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.

  7. Measure again

    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.

  8. Undo, if you ever want the factory curve back

    rm /etc/ustd/fan.conf && systemctl restart usdbd

    The fan goes back to about 2,000 RPM within seconds.

What the table means

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 temperatureFactoryThis recipe
under 60 °C20–30 %16 %everyday
60–70 °C30–36 %16 %warm day
70–80 °C36–44 %39 %
80–90 °C44–50 %70 %ramps sooner
90 °C and up50–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.

Why every line has the same start and end value

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.

One thing to watch

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.

After a firmware update

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.

The shortcut

If you trust me: one paste does it all

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.

Checks the modelStops without changing anything unless the router says it is a UDR.
Checks for old scriptsStops if another fan service is running, so nothing fights.
Keeps a backupSaves any existing fan table in /root before writing.
One-line undosh /root/quiet-fan.sh undo brings back the factory curve.
  1. Log in to the router as in step 1: ssh [email protected]
  2. Press Copy below, paste everything into the router window and press Enter.
  3. Wait about 30 seconds. At the end you should see 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 install

Later, 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.

Frequently asked questions

Why is my UniFi Dream Router fan so loud?

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.

Is it safe to slow down the UDR fan?

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.

Why not use a custom fancontrol script?

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.

Does the fix survive a firmware update?

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.

Does this work on the UDR7, UDM Pro or other UniFi consoles?

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.

How do I undo it?

Run rm /etc/ustd/fan.conf && systemctl restart usdbd, or sh /root/quiet-fan.sh undo. The factory curve is back within seconds.