Nextcloud & spun down drives

Once you have successfully installed Nextcloud, you might want to make sure your external drives stay spun down when you are not using them, to decrease power consumption & noise and increase lifetime.
I assume you got the timer already set (hdparm -S <something> or maybe using hd-idle), after which the drives will spin down if not used.

Nextcloud Cron job

However, there is a Nextcloud cron job run every 5 min, preventing the peaceful rest of your drives.
See also GitLab and Nextcloud forum.

I want to share here how I fixed this, while still executing this enough for garbage collection etc. to do its work.
You can add 'loglevel’ => 0 to /srv/www/nextcloud/config/config.php to have a look at which tasks are executed. You can find the logs (by default) in /srv/www/nextcloud/data/nextcloud.log, but there are easier to watch in the browser, under settings->admin settings->logging. Look for everything labelled cron in the App column.
I suspect from the names, also Nextcloud notifications are sent this way, so if you delay this process, keep this in mind.

Check if disks are spinning/in use & execute cron job

Method 1:

If your disk supports it, you can use something like hdparm -C ... or smartctl --info --nocheck=standby and only execute the job if the power mode is idle or active (in contrast to standby). You lucky bastard! :slight_smile:
The only thing you have to ensure is that this cron job isn’t faster than the timeout of your disks, otherwise they’ll never go to sleep, of course.

Method 2:

If you’re as unlucky as me and for some reason, you cannot query your disk power state, then I prepared some scripts to circumvent this.

The only way I found to accomplish this, is watching /srv for file accesses and if it is accessed, you assume the disks are spinning. As long as no access is made, you assume they are not spinning (or will soon stop).
I use inotify for this, I think ìnotifywait was readily installed on Turris OS, otherwise you can download it.

The following script blocks until there is some disk activity detected, all explanation inline:

/root/wait-for-disk-activity.sh

Edited, because kill 0 killed the whole script, resulting in never executing the rest… :confounded:

#!/bin/sh
# file: /root/wait-for-disk-activity.sh

EXEC_FILE=/tmp/run-on-disk-activity-services
PIPE="/tmp/wait-for-disk-activity-inotify-pipe-${USER}-$$.fifo"
# ensure named pipe is active (in RAM)
[ -p "${PIPE}" ] || mkfifo "${PIPE}"

# https://stackoverflow.com/questions/360201/how-do-i-kill-background-processes-jobs-when-my-shell-script-exits
trap 'exit' INT TERM
#trap 'kill 0' EXIT
trap '[ -p "${PIPE}" ] && rm "${PIPE}"' EXIT

# wait for something to happen on /srv, this ensures that disks aren't spun down
# actually, they might be spun down, because it seems that some things are cached in RAM, like when doing
# `ls -la /srv/www/nextcloud/data`
# but we assume filtering out the read-only events on directories is good enough
# The next command is not very efficient, because every time a read-only event on directories is catched, inotify will have to re-register it hooks (recursively!)
#while inotifywait --quiet --recursive /srv --format '%e' --syslog | grep -E "(OPEN,ISDIR|ACCESS,ISDIR|CLOSE_NOWRITE,CLOSE,ISDIR)" > /dev/null ; do : ; done

# this approach only registers the inotify hooks recursively 1x
# cannot use --daemon, otherwise I cannot cleanly close this process
inotifywait --quiet --recursive /srv --format '%e' --syslog --monitor --outfile "${PIPE}" &
grep -vE "(OPEN,ISDIR|ACCESS,ISDIR|CLOSE_NOWRITE,CLOSE,ISDIR)" "${PIPE}" -m 1 > /dev/null

# using named pipes, because just piping doesn't always exit cleanly - tested this manually
# (it needs another acces to /srv/ before exiting
# inotifywait --recursive /srv --format '%e' --monitor |  grep -vE "(OPEN,ISDIR|ACCESS,ISDIR|CLOSE_NOWRITE,CLOSE,ISDIR)" -m 1

This is used in the next script, where the normal operations are performed (see /usr/bin/nextcloud-cron.sh), if we are reasonable sure the disks are spinning anyway. This script can be run only ones, it locks itself as long as it is not yet finished (e.g. waiting for disk activity).

/root/nextcloud-cron-if-HDD-spin-up.sh

I’m not sure, but an unclean exit might keep the flock active, resulting in this script not running ever again

#!/bin/sh
# file: /root/nextcloud-cron-if-HDD-spin-up.sh

# lock this script, prevents multiple executions at the same time
# see `man flock`
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock --exclusive --nonblock "$0" "$0" "$@" || :
# `:` is a shell-builtin, no effect

# block until disk activity detected
/root/wait-for-disk-activity.sh

# same as /usr/bin/nextcloud-cron.sh
if [ -f /srv/www/nextcloud/config/config.php ]; then # doesn't wake up drive
        /usr/bin/php-cli -f /srv/www/nextcloud/cron.php # does wake up drive
fi

The new cron file. Every 5 min, this script is run, as was the case before. But now, it waits in the background until (you guessed it) the disks are in use. Every time we launch the script, but the previous one is still waiting, it just cleanly exits (flock). Thus, only ever 1 Nextcloud cron job will be active.

/etc/cron.d/nextcloud
# before (inactive)
#*/5  *  *  *  *  nobody /usr/bin/nextcloud-cron.sh
# after (active)
 */5  *  *  *  *  nobody /root/nextcloud-cron-if-HDD-spin-up.sh

# extra idea:
# spin up every day at a specified time, to enable nextcloud notifications & garbage collection
# ... nobody <some command triggering wait-for-disk-activity script, maybe like `touch /srv/www/nextcloud/cron.php>

This is my first how-to/guide ever, so any feedback is very welcome, certainly if this made your day!

EDIT to wait-for-disk-activity.sh, otherwise this guide didn’t do anything…

5 Likes