User Tools

Site Tools


synologyautoshutdownscriptnetworkload

Synology: Networkload based automatic DSM shutdown via script with Tux

Hey, my name is "Tux"! Have you ever had the problem you want your Synology NAS to be powered off automatically when it's not in use but you did not set up an automated shutdown cycle because of you could not be sure whether there are still some network related jobs up and running?

Hey, my name is "Tux" and this tutorial will show you how you can control your Synology NAS via bash script in a more power saving way so that it will automatically shutdown due to a specific amount of data (not) being transferred over specific Synology NAS' network interface(s) within a specific amount of time.



First thoughts

Note

This tutorial is not for everyone! It is basically for people who want their NAS being turned off to save power when it is not in use over network.

Important note!

Be aware that using this script is not recommended in case you are using your Synology NAS' any other physical data interface than LAN (e.g. USB, eSATA, …) because all other physical data interfaces than LAN will be ignored by this script and therefore the Synology NAS will shutdown even if you have a running service over physical non-LAN interfaces! In addition, keep in mind, that lots of startup/shutdown cycles can effect the lifetime of your Synology NAS, including the data drives. YOU HAVE BEEN WARNED!

Important note!

Please note that in this tutorial the sshpass-plugin for unattended user/password authentication is being used as in my case the script is only being used on secure home environment. Do NOT use this method in production environmant due to security risks! In this case you should use the SSH certificate authentication mode instead! In this case please look out for the two following script code lines containing the sshpass-command and just do the according following small one-line modification on both of the lines:

Script modification for certificate authentication

sshpass -p "$hostRootPassword" ssh -p "$hostSSHPort" root@"$host"…

Basic pre-information

Unfortunately there is no advanced SNMP-client on the Synology NAS pre-installed but only an SNMP-server. This means you cannot run the script on DSM localhost (127.0.0.1) unless you'll find an SNMP-client package for DSM to install. That said, it is necessary to put the script onto a remote Linux system (Ubuntu 18.04 server-VM on VMware ESXi-host in my case) which on the other hand keeps your Synology NAS save from ending in a shutdown loop in case of script misconfiguration. Of course you could also place an Ubuntu-VM onto the Synology NAS itself when using Synology's Virtual Machine Manager, this would make some code lines inside the script unnecessary (e.g. PING commands) but would work anyway, so feel free to adjust the script if you want or leave it untouched in any case, it does not affect the script's functionality.

This tutorial is based on the following two systems which was tested successfully according to its functionality:

  • Ubuntu server 20.04
  • Synology DSM 7.0


Start of tutorial

System preparation

Synology NAS

On Synology:
Enable SSH and SNMP service on the Synology NAS (Control panelTerminal & SNMP) and on SNMP settings give it a meaningful custom community name (for example your Synology's hostname) which later has to match the according script variable value. You also have to enable SNMPv1, SNMPv2c service. Also make sure your Synology NAS' firewall rules are set up to allow incoming SSH traffic (port 22) SNMP traffic (port 161) and ICMP (PING) traffic!

Enable SNMP service on Synology and add a community name
Firewall rule seetings for SNMP and SSH service
Firewall rule settings for ICMP


Now make sure that the root user on your Synology NAS has permissions to login directly. To accomplish this, please follow this tutorial.

Linux SNMP-client system

On your Linux client install the SNMP-client:

$ apt install snmp

Optional: On your Linux client download and install the sshpass package:

$ apt-get install sshpass

Now, before go closer to the implementation, it is important to know about best practice on how to start the first time with this script: First of all, like already mentioned, you already had to decide whether it makes sense for you to use the script at all because it is network traffic dependent only. This basically means, when a specific amount of data is not being transferred over the Synology NAS' network ports over a specific amount of time, you can reliably assume that the Synology NAS is not in use (which you could not when using any other physical data interface other than LAN). As in my case, all my interactions with my Synology NAS are always heavy network traffic dependent (automated weekly backup jobs, copying large/a lot of files over SMB/NFS, media server, and so on…) and I want to have my Synology NAS powered off when none of those jobs are running. So this script is exactly what I want to have because it lets me automate the Synology NAS' power control completely. So now, if you are willing to use the script too (which I assume you do when you've reached over here), you have to decide how long you want to have your Synology NAS running idle before it shuts down automatically. To give you an idea, in my case, I took one hour (for the script that would be a value of 3600 seconds) and a shutdown-limit of 500 MB (for the script that would be a value of 500000000 Bytes) for every single of the Synology NAS' network interfaces.

Example screenshot from my personal script settings for 500 MB within 1 hour

This means: If the amount of data being transferred over any of the Synology NAS' network ports does not exceed 500 MB within one hour, the Synology NAS is going to shutdown.

For your specific case you have to decide two things:

  1. The time you want to give to your Nynology NAS being "idle" before it shuts down. Don't choose this value too low and always keep in mind to check whether the chosen time could cross any of your already set up automated jobs (e.g. backup jobs). For example: If you turn on your Synology NAS automatically to run two backup jobs and you have set up one hour of time-delay between those backup jobs but the first backup job has finished after 10 minutes, you really don't want the script to shutdown the Synology NAS 30 minutes after the first backup job because in this case the second backup job would not be executed. So in this specific case the script's time value should have to be set to at least more than 3600 seconds.
  2. The amount of data you want to set that allows you to assume your Synology NAS it not being used (500 MB within one hour first seemed a lot to me for being idle first. But as I did some tests I mentioned that in my case the amount of data being transferred over the Synology NAS' "eth3"-port, which in my case is used for iSCSI connections from my Synology NAS to my VMware ESXi host only, does exceed 300 MB, even when there are no VMs connected to that iSCSI storage, so I gave it +200 MB to make sure the shutdown-limit is not set too close). That said, to find out how many data per network interface is being transferred since the last Synology NAS reboot in your case, just activate the "file logging" option inside the script (which is disabled by default) and it will log the amount of data being transferred per network interface into a file inside the according user's home folder (incoming and outgoing traffic will be logged separately and twice: first time as soon as the Synology NAS is reachable over PING and the second one after the time you have set on the global "sleepTime" variable). Just make sure your Synology NAS is not under network load while logging.
File logging option. Change the value to "y" to activate file logging and don't foregt to set it back after your testing phase has finished!
Global "sleepTime" variable, set to 1 hour (3600 secons) in this specific case.

Note: You can even do a log history cycle over night or even a few days if you want because I've implemented a counter which will increase after every loop pass. This counter value is also being added to the filename(s) being created, just study the script a little bit and you'll find out what it does if you are interested in it.

Now there are a few more things to mention:

You may think that I was doing it too granular when getting SNMP values twice for every of my four network interfaces and even differentiate incoming and outgoing traffic. Of course you could also add up all values together twice and then do a single substraction but when you leave the script almost untouched and use it like I did you will be more flexible if, for example, you would want to exclude a specific network interface from being monitored for whatever reason.

Now, let's stop messing around and just start with the script!

First, according to official Synology MIB documents, to find out the specific SNMP-OID and the according network interface name per network interface, from an SNMP client Linux system, execute the following command (for <mySNMPCommunity> set the community name value as configured recently):

$ snmpwalk -v2c -c "<mySNMPCommunity>" "<mySynologyIPAddress>" .1.3.6.1.2.1.31.1.1.1.1

Now customize all required script parameters (starting with <my…) on the following script, including replacing the SNMP-OIDs which you should got now from the command output above. The following script is based on four network interfaces, just customize the amount if you have more/less network interfaces to process.

#!/bin/bash
#
# By default this script assumes 4 network interfaces to process.
# If you have to process more/less than 4 network interfaces please look out for the three "##++/--" characters
# inside the script code which are marking where you have to do the according modifications (basically it is just
# adding/removing existing codelines).
#
# --------------------------- GENERAL SETTINGS ---------------------------
# Startup delay in seconds:
sleep 5
# Synology NAS SNMP community:
community="<myCommunity>"
# Synology NAS IP address:
host="<myIPAddress>"
# Synology SSH port number:
hostSSHPort="<mySSHPort>"
# Synology NAS root password:
hostRootPassword="<myRootPassword>"
# Synology NAS' data shutdown-limit per network interface in bytes:
shutdownLimit="<myShutdownLimit>"
# Sleeptime between stage 1 and stage 2 in seconds:
sleepTime="<mySleeptime>"
# Declare a general associative array:
declare -A array
#
# Set interface name(s) for the according Synology network interfaces to be monitored.
# You are free to choose a random names without any effection on the scripts' functionality
# but it is recommended to match the typical descriptions like the ones you'll get when you search
# through SNMP tables, e.g. "eth(x)".
# Note: It is also possible to monitor virtual network interfaces.
# Example:
# if1="eth0"
if1="<myNetworkInterfaceName1>"
if2="<myNetworkInterfaceName2>"
if3="<myNetworkInterfaceName3>"
if4="<myNetworkInterfaceName4>"
##++/--
#
# Declare OID variables for [IN]coming network traffic:
# Example:
# declare "$if1"_IN_OID_var="iso.3.6.1.2.1.31.1.1.1.6.3"
declare "$if1"_IN_OID_var="<mySynologyNetworkInterface1_IN_OID>"
declare "$if2"_IN_OID_var="<mySynologyNetworkInterface2_IN_OID>"
declare "$if3"_IN_OID_var="<mySynologyNetworkInterface3_IN_OID>"
declare "$if4"_IN_OID_var="<mySynologyNetworkInterface4_IN_OID>"
##++/--
# Set OID variables for [IN]coming network traffic:
if1_IN_OID="$if1"_IN_OID_var
if2_IN_OID="$if2"_IN_OID_var
if3_IN_OID="$if3"_IN_OID_var
if4_IN_OID="$if4"_IN_OID_var
##++/--
# Declare OID variables for [OUT]going network traffic:
declare "$if1"_OUT_OID_var="<mySynologyNetworkInterface1_OUT_OID>"
declare "$if2"_OUT_OID_var="<mySynologyNetworkInterface1_OUT_OID>"
declare "$if3"_OUT_OID_var="<mySynologyNetworkInterface1_OUT_OID>"
declare "$if4"_OUT_OID_var="<mySynologyNetworkInterface1_OUT_OID>"
##++/--
# Set OID variables for [OUT]going network traffic:
if1_OUT_OID="$if1"_OUT_OID_var
if2_OUT_OID="$if2"_OUT_OID_var
if3_OUT_OID="$if3"_OUT_OID_var
if4_OUT_OID="$if4"_OUT_OID_var
##++/--
#
# --------------------------- OPTIONAL SETTINGS ---------------------------
#
# --------------------- File logging -------------------------
# Activate "file logging" ( ["y"]es / ["n"]o )? (Default = no)
# BEWARE: If activated, according to your settings, this may
# create a lot of files in the according user's home folder!
# You should activate this only for testing/analyzing purposes!
fileLogging="n"
# --------------------- Soft mode -------------------------
# Activate "test mode" ( ["y"]es / ["n"]o )? (Default = no)
# If activated, the script will never execute the shutdown
# command for the Synology NAS. Therefore the NAS will never
# physically shut down but will return an echo value "powerOff!".
# Activate this option for command line testing purposes if needed.
testMode="n"
# --------------------- Curfew time mode -------------------------
# Activate "Curfew time mode" ( ["y"]es / ["n"]o )? (Default = no)
# If activated, you can enter a specific weekday number (MO=1, SU=7),
# a specific start- and an end-hour in which the Synology NAS will
# be prevented from shut down, for example if you want to specifiy
# a time range for your regularly data backup cycles where you want
# this script to be inactive.
# Note: It would not make sense to go more granular than hours,
# so minutes and even seconds have been left over.
# Feel free to adjust the script if you really want minutes and/or
# seconds to be implemented.
# Also feel free to add more than a single curfew time mode, in this
# case please adjust the script for your specific requirements.
curfewTimeMode="n"
curfewTimeDay="<myCurfewTimeDay>"
curfewTimeStartHour="<myCurfewTimeStartHour>"
curfewTimeEndHour="<myCurfewTimeEndHour>"
softMode="n"
# ---------------------------------------------------------
#
# --------------------------- START SCRIPT LOOP ---------------------------
#
while true; do
	# Test mode check:
	if [ "$testMode" == "y" ]; then
		echo "testModeEnabled!" && echo $(date)
	fi
	# Increase loop counter:
	let "loopCounter=loopCounter+1"
	echo "Loop-No.: "$loopCounter"" && echo $(date)
	#
	# *** Stage 1 ***
	#
	# Check whether the system is reachable over network...
	ping -c 1 "$host" > /dev/null
	hostPingResult="$?"
	# ...if yes:
	case "$hostPingResult" in
		0)
		echo "Stage 1: case 0: pingResponse!" && echo $(date)
		# Get the system uptime since last boot in seconds:
		hostUptimeCheck=$(sshpass -p "$hostRootPassword" ssh -p "$hostSSHPort" root@"$host" "awk '{print \$1}' /proc/uptime")
		# If system uptime since first successful PING response is less than 120 seconds, assume it has recently (re)booted.
		# In this case wait for 60 seconds to make sure the system's SNMP-services are ready:
		if [ "${hostUptimeCheck%.*}" -lt "120" ]; then
			echo "System has recently (re)booted, sleep for 60 seconds!" && echo $(date)
			sleep 60
		fi
		# Get SNMP stage 1 values for the according eth(x)-interfaces for [IN]coming/[OUT]going traffic since the last system reboot.
		#
		# *** [IN]coming traffic ***
		array["$if1"_IN_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if1_IN_OID})
		array["$if2"_IN_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if2_IN_OID})
		array["$if3"_IN_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if3_IN_OID})
		array["$if4"_IN_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if4_IN_OID})
		##++/--
		#
		# *** [OUT]going traffic
		array["$if1"_OUT_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if1_OUT_OID})
		array["$if2"_OUT_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if2_OUT_OID})
		array["$if3"_OUT_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if3_OUT_OID})
		array["$if4"_OUT_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if4_OUT_OID})
		##++/--
		#
		Sleeptime between stage 1 and stage 2 (between measuring points):
		echo "Entering stage 2 in "$sleepTime" seconds!" && echo $(date)
		sleep "$sleepTime"
		#
		# *** Stage 2 ***
		ping -c 1 "$host" > /dev/null
		hostPingResult="$?"
		case "$hostPingResult" in
			0)
			echo "Stage 2: case 0: pingResponse!" && echo $(date)
			# Get SNMP stage 2 values for the according eth(x)-interfaces for [IN]coming/[OUT]going traffic since the last system reboot.
			#
			# *** [IN]coming traffic ***
			array["$if1"_IN_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if1_IN_OID})
			array["$if2"_IN_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if2_IN_OID})
			array["$if3"_IN_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if3_IN_OID})
			array["$if4"_IN_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if4_IN_OID})
			##++/--
			#
			# *** [OUT]going traffic
			array["$if1"_OUT_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if1_OUT_OID})
			array["$if2"_OUT_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if2_OUT_OID})
			array["$if3"_OUT_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if3_OUT_OID})
			array["$if4"_OUT_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if4_OUT_OID})
			##++/--
			#
			# Check whether the system has rebooted between stage 1 and stage 2:
			if [ "${array["$if1"_IN_stage1]}" -le "${array["$if1"_IN_stage2]}" ] && \
			   [ "${array["$if2"_IN_stage1]}" -le "${array["$if2"_IN_stage2]}" ] && \
			   [ "${array["$if3"_IN_stage1]}" -le "${array["$if3"_IN_stage2]}" ] && \
			   [ "${array["$if4"_IN_stage1]}" -le "${array["$if4"_IN_stage2]}" ] && \
			   [ "${array["$if1"_OUT_stage1]}" -le "${array["$if1"_OUT_stage2]}" ] && \
			   [ "${array["$if2"_OUT_stage1]}" -le "${array["$if2"_OUT_stage2]}" ] && \
			   [ "${array["$if3"_OUT_stage1]}" -le "${array["$if3"_OUT_stage2]}" ] && \
			   [ "${array["$if4"_OUT_stage1]}" -le "${array["$if4"_OUT_stage2]}" ]; then
			   ##++/--
				#
				# Calculate the effective amount of transferred [IN]coming and [OUT]going data per network interface:
				array["$if1"_diff_IN]=$((${array["$if1"_IN_stage2]} - ${array["$if1"_IN_stage1]}))
				array["$if2"_diff_IN]=$((${array["$if2"_IN_stage2]} - ${array["$if2"_IN_stage1]}))
				array["$if3"_diff_IN]=$((${array["$if3"_IN_stage2]} - ${array["$if3"_IN_stage1]}))
				array["$if4"_diff_IN]=$((${array["$if4"_IN_stage2]} - ${array["$if4"_IN_stage1]}))
				array["$if1"_diff_OUT]=$((${array["$if1"_OUT_stage2]} - ${array["$if1"_OUT_stage1]}))
				array["$if2"_diff_OUT]=$((${array["$if2"_OUT_stage2]} - ${array["$if2"_OUT_stage1]}))
				array["$if3"_diff_OUT]=$((${array["$if3"_OUT_stage2]} - ${array["$if3"_OUT_stage1]}))
				array["$if4"_diff_OUT]=$((${array["$if4"_OUT_stage2]} - ${array["$if4"_OUT_stage1]}))
				##++/--
				#
				# File logging:
				if [ "$fileLogging" == "y" ]; then
					printf "${array["$if1"_diff_IN]}" > ~/"$if1"_LoopNo"$loopCounter"_IN_transferredData.txt
					printf "${array["$if2"_diff_IN]}" > ~/"$if2"_LoopNo"$loopCounter"_IN_transferredData.txt
					printf "${array["$if3"_diff_IN]}" > ~/"$if3"_LoopNo"$loopCounter"_IN_transferredData.txt
					printf "${array["$if4"_diff_IN]}" > ~/"$if4"_LoopNo"$loopCounter"_IN_transferredData.txt
					printf "${array["$if1"_diff_OUT]}" > ~/"$if1"_LoopNo"$loopCounter"_OUT_transferredData.txt
					printf "${array["$if2"_diff_OUT]}" > ~/"$if2"_LoopNo"$loopCounter"_OUT_transferredData.txt
					printf "${array["$if3"_diff_OUT]}" > ~/"$if3"_LoopNo"$loopCounter"_OUT_transferredData.txt
					printf "${array["$if4"_diff_OUT]}" > ~/"$if4"_LoopNo"$loopCounter"_OUT_transferredData.txt
					##++/--
				fi
				#
				# Check per network interface if data shutdown-limit has been exceeded (if yes, shutdown the System):
				if [ "${array["$if1"_diff_IN]}" -lt "$shutdownLimit" ] && \
				   [ "${array["$if2"_diff_IN]}" -lt "$shutdownLimit" ] && \
				   [ "${array["$if3"_diff_IN]}" -lt "$shutdownLimit" ] && \
				   [ "${array["$if4"_diff_IN]}" -lt "$shutdownLimit" ] && \
				   [ "${array["$if1"_diff_OUT]}" -lt "$shutdownLimit" ] && \
				   [ "${array["$if2"_diff_OUT]}" -lt "$shutdownLimit" ] && \
				   [ "${array["$if3"_diff_OUT]}" -lt "$shutdownLimit" ] && \
				   [ "${array["$if4"_diff_OUT]}" -lt "$shutdownLimit" ]; then
				   ##++/--
					# Curfew time mode check:
					case "$curfewTimeMode" in
						y)
						# Check current day and hour:
						currentDay=$(date +%u)
						currentHour=$(date +%H)
						# Check whether current day and hour is within curfew time:
						if [ "$currentDay" == "$curfewTimeDay" ] && [ "$currentHour" -ge "$curfewTimeStartHour" ] && [ "$currentHour" -le "$curfewTimeEndHour" ]; then
							softMode="y"
							echo "softModeEnabled!" && echo $(date)
						# Test mode check:
						elif [ "$testMode" == "n" ]; then
							softMode="n"
						fi
						;;
						n)
						;;
					esac
					# Soft mode check:
					case "$softMode" in
						n)
						# Test mode check:
						if [ "$testMode" == "n" ]; then
							sshpass -p "$hostRootPassword" ssh -p "$hostSSHPort" root@"$host" 'sudo poweroff'
							# After "powerOff" command was executed wait 30 seconds before continuing to make sure all system's network interfaces were shut down properly and therefore will not send any PING reply anymore.
							# This prevents this script from entering into "Stage 1" unnecessarily again:
							echo "hardMode powerOff! Sleep for 30 seconds!" && echo $(date)
							sleep 30
						fi
						echo "(testmode)hardMode powerOff!" && echo $(date)
						;;
						y)
						echo "softMode powerOff!" && echo $(date)
						;;
					esac
				fi
			else
				echo "System has rebooted between stage 1 and stage 2!" && echo $(date)
			fi
			;;
			1)
			echo "Stage 2: case 1: noPingResponse!" && echo $(date)
			;;
			2)
			echo "Stage 2: case 2: pingError!" && echo $(date)
			;;
		esac
		;;
		1)
		echo "Stage 1: case 1: noPingResponse!" && echo $(date)
		;;
		2)
		echo "Stage 1: case 2: pingError!" && echo $(date)
		;;
	esac
	# Delay before repeating the script to prevent uneccessary load.
	echo "Sleep for 60 seconds before repeating loop!" && echo $(date)
	sleep 60
	echo "endOfLoop!" && echo $(date)
done;

After configuring all of the script's parameters according to your requirements you have to put the script onto your Linux SNMP-client system. Then make the script executable:

$ chmod +x <myScript>.sh

Then run the script manually to do all required testings to figure out your specific settings for later automation.

After those testings you should have figured out the matching script values for your personal requirements. So after configuring the script according to your requirements open the crontab editor on your Linux SNMP-client system:

$ crontab -e

Now, to make the script run automatically on system reboot, which would be best practice in most cases, just insert the following line to the crontab file to add the specific cronjob:

@reboot /<myPathToMyScript>/<myScript>.sh

…or alternatively, if you want to log the script's output (stdout and stderr) into a file permanently (the logfile will be deleted after every system reboot when it is saved into the /tmp directory, you have to put it into another path (e.g. /home/<myUser>) if you don't want it to be deleted on reboot):

@reboot sleep 30 && /<myPathToMyScript>/<myScript>.sh >> /tmp/<myScript>.log 2>&1

Now save it and quit the crontab editor.

This way the script will start automatically everytime you reboot your Linux SNMP-client system and your networkload based Synology NAS shutdown script has successfully been set up.


End of tutorial



Appreciate my work?
Buy me a coffee or PayPal

synologyautoshutdownscriptnetworkload.txt · Last modified: 2023/04/22