I'd like to have my hard drives spin down when idle for, let's say 20 minutes. Any number of minutes is fine, as this NAS is rarely used.

What I've tried and didn't work:

  • ataidle -S 20 /dev/ada0 simply spins down the drive immediately and the timer has no effect. Once drives are spun back up due to an access they stay spinning.

  • camcontrol standby /dev/ada0 -t 1200 same behavior as ataidle.

  • FreeNAS UI's Storage -> Disks -> Adv. Power Manager setting simply calls camcontrol and similarly the timer has no effect. If a power setting that allows standby is selected (such as 127) then drives spin down nearly immediately (potentially after 8 seconds), and get constantly spun up and down if there are any accesses. UPDATE: See How to get FreeNAS to spin down disks? for instructions on how to make this work and skip the manual scripts.

How can I get the normal expected "stand by if not used in a while" behavior?

Using FreeBSD 11.2-STABLE via FreeNAS 11.2. Drives are 4x Samsung 2TB 2.5" .T2000LM003`

# smartctl -P show /dev/ada0
smartctl 6.6 2017-11-05 r4594 [FreeBSD 11.2-STABLE amd64] (local build)
Copyright (C) 2002-17, Bruce Allen, Christian Franke, www.smartmontools.org

Drive found in smartmontools Database.  Drive identity strings:
MODEL:              ST2000LM003 HN-M201RAD
FIRMWARE:           2BC10007
match smartmontools Drive Database entry:
MODEL REGEXP:       ST(1500|2000)LM0(03|04|06|07|10) HN-M[0-9]*RAD
MODEL FAMILY:       Seagate Samsung SpinPoint M9T
ATTRIBUTE OPTIONS:  None preset; no -v options are required.
  • Are you sure they don't already spin down when idle? Have you verified you have absolutely nothing reading/writing to the drives? IE, you need to make sure you don't have any logs writing things, any cron processes that might read or write. Any daemons touch the drives/etc. – Zoredache May 21 '19 at 23:53
  • If I don't explicitly access the drives then they stay spun down. The first ls wakes them up, then afterwards they stay spun up. – Adam May 22 '19 at 1:20

Thanks for this idea. I had been struggling with this problem for a few days. As I am not used to write bash scripts I rewrote it in python3. Maybe somebody finds it useful:

import subprocess
import time

drives_to_check = ['ada3', 'ada4', 'ada5']
no_sleep_hours = ['00', '01']
seconds = 600

if time.strftime("%H") in no_sleep_hours:

o = subprocess.check_output(f'/usr/sbin/iostat -x -z -d {seconds} 2', shell=True)

for drive in drives_to_check:
    if drive not in o.decode().split('extended')[-1]:
        p = subprocess.check_output(f'/sbin/camcontrol cmd {drive} -a "E5 00 00 00 00 00 00 00 00 00 00 00" -r -', shell=True)
        if p.decode()[27:29] != '00':
            q = subprocess.check_output(f'/usr/local/sbin/ataidle -s /dev/{drive}', shell=True)

My small add-ons were: you can add hours to no_sleep_hours when you don't want the script to run. And you have to add the drives you want to check to "drives_to_check" so you can exclude drives that should not do a spindown. Maybe you would have to adjust the paths for iostat and ataidle, these are the paths used in FreeNAS. You can find your paths with: which adaidle or which iostat. You can add it to cron with */15 * * * * /usr/local/bin/python3 /path/to/the/folder/check_disk_usage_and_spindown.py

  • 1
    nice script! It sure is easier to parse output in Python than it is in Bash. I'd suggest adding some comments to your script, though, both for yourself and for others trying to learn from you. Your script largely does the same thing as mine, but there are some important differences. In some ways yours is better, but a few strategic comments can point out what your script does so someone who hasn't been knee deep in these commands can notice and modify the script to their system and/or needs. – Adam Jun 4 '19 at 22:28

UPDATE: See https://serverfault.com/a/965694/184095 for how to make the GUI work and skip the manual scripting process below.

I ended up doing this very manually. Write a script that checks for drive activity, spins down drives if there is none, then schedule this script as a cron job.

I settled on an approach that uses iostat to monitor drives for a set period of time, in my case 600 seconds (10 minutes). If iostat reports no usage, call ataidle -s /dev/ada[0123] to suspend the drives. I setup a cron job to call this script every 15 minutes: */15 * * * * spindown_if_idle.sh 600



# argument is the number of seconds to test for drive use, default to 5
if [ -z "$1" ]; then

function list_ada_drives {
    # emit a list of hard drives, i.e. ada0 ada1 ada2 ...
    iostat -x | grep "ada" | awk '{print $1}'

function spindown {
    # for every hard drive use ataidle to place it in standby
    for ADA in $(list_ada_drives); do
        ataidle -s /dev/$ADA

function are_drives_used {
    # argument is number of seconds to test over

    # run iostat for the specified number of seconds and ask to report any usage
    # -z means to omit any drives that are idle
    # iostat will print two reports, the first since boot and the second during the interval
    # The first tail and grep strip off the first useless report. The second tail strips off the header
    # The final grep strips off drives we're not interested in
    iostat -x -z -d $SECONDS 2 | tail -n +2 | grep -A5000  "extended" | tail -n +3 | grep "ada" -q

if are_drives_used $SECONDS ; then
    echo "Drives are being used"
    echo "Drives are idle, going to standby"

collectd metrics

I tried avoiding that 10-minute block by querying historic usage as recorded by collectd. I used rrdtool fetch to query the metrics stored in /var/db/collectd/rrd/localhost/disk-ada0, but those have a lag of several minutes. Relying on them would mean that the script might standby drives that are actively used if they had been idle recently.

Testing if drives are spun up

The following script will report if each drive is idle or spinning. Useful for testing.



camcontrol devlist | grep ada | awk -F\( '{print $2'} | awk -F",|\\\\)" '{print $2}' |while read LINE
CM=$(camcontrol cmd $LINE -a "E5 00 00 00 00 00 00 00 00 00 00 00" -r - | awk '{print $10}')
if [ "$CM" = "FF" ] ; then
elif [ "$CM" = "00" ] ; then
echo "$LINE: IDLE"

Based on this forum post.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.