Automatically suspend selfmade NAS when disks are idle

I’ve recently had the urge to have a NAS as my central data storage. For typical home workloads (for me anyway) the home network speeds are good enough and having my data in a central space means I don’t have to worry about hard-drive failure in my laptop and desktop PC. Setting up regular backups is also a lot easier.

Instead of buying a NAS I decided to recycle one of my old machines and build my own. While this saves money, it also means I’m missing features I would usually get out-of-the-box.

I’ve installed a fresh headless copy of Debian 10 and got to work.

My NAS doesn’t need to run 24/7, it just needs to be available 24/7. What do I mean by this? I don’t use my computer non-stop all day. During the times of day where I don’t use the files on my NAS, be it because I’m sleeping or just browsing the web, the drives can be spun down and the NAS itself can go to sleep. However, it needs to wake up and be available once I decide I need access to the files.

Automatically spin down drives

Debian 10 comes pre-installed with hdparm. It allows you to see the current state of a drive and set certain parameters, one of which is the amount of idle time before the drive spins down.

root@nas:~$ hdparm -S 120 /dev/sdb

/dev/sdb:   
    setting standby to 120 (10 minutes)   

Setting the standby time you want requires a bit of math. A value of 0 prevents the drive from ever going into standby. Values between 1 and 240 are multiplied by 5 seconds and values between 241 and 251 increase in 30 minute steps (241 = 30min, 242 = 60min,…).

These settings are not permanent, which is why I’ve written a script to set them.

#!/bin/bash  
  
for DRIVE in \  
    "/dev/sdb" \  
    "/dev/sdc"  
do  
    hdparm -S 120 $DRIVE  
done   

Automatically suspend when all drives are spun down

Now that the drives automatically spin down when they’re not in use we can work on letting the whole system go to sleep. The easiest way (that I’ve found) is setting up a cronjob with another script.

#!/bin/bash  
  
for DRIVE in \
    "/dev/sdb" \ 
    "/dev/sdc"  
do  
    if [ -z "$(hdparm -C $DRIVE | grep standby)" ]; then  
        # $DRIVE is not in standby  
        exit 0  
    fi  
done  
  
systemctl suspend  

If any of the drives is not in standby the script exits early. If all drives are in standby the script sends the NAS to sleep.

All that is left to do is to set up the cronjob to run the script every 5 minutes.

*/5 * * * * /usr/local/bin/suspend_on_inactivity > /dev/null 2>&1  

Make it work after sleeping

For this to work you have to activate “Wake On Lan” in your BIOS / UEFI.

When the NAS receives the WOL packet it wakes up and returns to the state it was in when going to sleep. However, the settings for our drives are not recovered, which means we need to run our script again.

Debian 10 uses systemd, which means that we can create a new service that runs every time the NAS boots up. The following is all we need.

[Unit]  
Description=Sets hdparm sleep timers  
After=suspend.target  
  
[Service]  
Type=oneshot  
ExecStart=/usr/local/bin/set_hdparm  
  
[Install]  
WantedBy=multi-user.target suspend.target  

All that is left to do is to enable and start the newly created service.

systemctl enable hdparm.service  
systemctl start hdparm.service