Backup and Restore Raspberry Pi to Synology DiskStation

Introduction

There are many articles on how to backup Raspery Pi systems, up to now I have relied on taking card images and storing these on a machine that is backed up. However it is all to easy to have a few hours of fun with the Raspberry Pi and then not get round to making a backup, I want something that is automated so that I don’t have to think about it.
The key goals for this backup strategy are:
1) Unattended backup
2) Warnings sent for backup failure [ToDo]
3) Rotating backups
4) Backup to my Synology DiskStation
5) Simple to add additional Raspberry Pi’s to the backup

References

None of this is particularly new, I have pulled together ideas that I’ve found in a number of other sources, all of these were helpful

Solution Overview

I have a Synology DiskStation with Raid disks that I backup all of my machines to, this post backs up to this server, the same basic approach should work going to any Linux server with small modifications.
cron is used on the server to invoke a script to perform the backup, the script is parameterised with the ip address of the Raspberry Pi to backup.
rsync is used to perform the actual backup.
ssh is used to securely connect to the Raspberry Pi.

Backing Up

Step 1 – root SSH access

We need the Synology DiskStation to have root access via SSH to the Raspberry Pi. Open up two SSH sessions, one to the DiskStation (as root) and one to the Pi (as your normal login e.g. pi).
1) In the Raspberry Pi Session first enable root login by setting a root password

sudo passwd root

Enter a password as prompted
Now we need to ensure that there is a directory to hold ssh keys.

su root

mkdir ~/.ssh

exit

2) In DiskStation session
First check to see if you already have a pair of SSH keys by running

ls ~/.ssh

 
If you don’t see a file called id_rsa.pub there you need to generate a pair of keys using this command

ssh-keygen -t rsa -C root@<Your Synology server’s name>

– When prompted for the file in which to save the key, accept the default (hit <Enter>)
– When prompted for a passphrase hit <Enter> (no passphrase)

Now push the public key to the Raspberry Pi (this is why we need the Pi to briefly allow password logins on the root account, because until the public key exists on the destination server, you will be prompted for a password when you run this command):

cat ~/.ssh/id_rsa.pub | ssh root@<your Pi’s IP address> ‘cat >> .ssh/authorized_keys’

Enter the password you created in Step 1 when prompted

Confirm that SSH key logins are now accepted by the Raspberry Pi by running this command (still from the Synology session, don’t toggle back to the Pi session yet🙂

ssh root@<Your Pi’s IP Address>

Now you can close the Synology session’s terminal window
 
3) In the Raspberry Pi Session remove password based root access. 

passwd -d root

Be very careful to do this on the Raspberry Pi – you could cause all sorts of problems doing this from another one of your hosts.
Now close down this ssh session as well
 

Step 2 Creating the backup script files

Using a web browser log in to the DiskStation as admin. Create a top level folder for holding all files related to Raspberry Pi backups (rpi_backup), use the Control Panel->Shared Folder
Then using the File Station create a directory under rpi_backup called scripts, and another called logs.
NOTE: I think the easiest way to create these scripts is to use the Text Editor from the Main Menu on the DiskStation, alternately use ssh to connect to the DiskStation and then use vi to create the files.
Create a file called rpi_backup/scripts/backup_pi.sh with the following content.

#!/bin/ash
#This script uses rsync to backup a named Raspberry Pi at a given IP address
#Three rotating backups are kept. If rsync fails then the preceding backups are retained.
if [ “$#” -ne 2 ] ; then
echo “Usage: 2 arguments required: $0 SERVERNAME IP” >&2
exit 1
fi
# Set up string variables
SERVER=$1
ADDRESS=$2
NOW=$(date +”%Y-%m-%d”)
RPI_BACKUP=”/volume1/rpi_backup”
LOGFILE=”$RPI_BACKUP/logs/$SERVER-$NOW.log”
SERVERDIR=”$RPI_BACKUP/$SERVER”
BASENAME=”$SERVERDIR/$SERVER”
# Paths to common commands used
MV=/bin/mv;
RM=/bin/rm;
MKDIR=/bin/mkdir;
PING=/bin/ping;
RSYNC=/usr/syno/bin/rsync
#Function to check for command failure and exit if there has been one. This is done
# so that when invoked from cron the error is reported
check_exit_code() {
exit_code=$?
if [ “$exit_code” -ne “0” ] ; then
echo “$1”
echo “exit with exitcode $exit_code”
exit 1
fi
}
#Ping the RPI a few times to ensure the interface is up (I’ve not seen this fail)
$PING $ADDRESS -c 3 >> $LOGFILE
# Ensure we have a top level backup directory for this server
if ! [ -d $SERVERDIR ] ; then
$MKDIR $SERVERDIR ;
fi ;
# Backups are made to BASENAME.0, this is then moved if the backup was successfull.
# So we start by clearing out anything from a failed backup
if [ -d $BASENAME.0 ] ; then
$RM -rf $BASENAME.0 ;
fi;
# RSYNC via SSH from the RPI as an incremental against the previous backup.
$RSYNC -av
–delete
–exclude-from=$RPI_BACKUP/scripts/rsync-exclude.txt
–link-dest $BASENAME.1
-e “ssh -p 22” root@$ADDRESS:/
$BASENAME.0 >> $LOGFILE 2>&1
# If RSYNC failed in any way, don’t trust the backup, exit the script
check_exit_code “RSYNC Failed”
#Rotate the existing backups
if [ -d $BASENAME.3 ] ; then
$RM -rf $BASENAME.3 ;
fi;
if [ -d $BASENAME.2 ] ; then
$MV $BASENAME.2 $BASENAME.3 ;
fi;
if [ -d $BASENAME.1 ] ; then
$MV $BASENAME.1 $BASENAME.2 ;
fi;
if [ -d $BASENAME.0 ] ; then
$MV $BASENAME.0 $BASENAME.1 ;
fi;

Create a second file called rpi_backup/scripts/rsync-exclude.txt with this content

/proc/*
/sys/*
/dev/*
/boot/*
/tmp/*
/run/*
/mnt/*

At this stage I like to manually run the script to ensure that it is working as I like. To do this connect to the DiskStation as root via ssh, cd to /volume1/rpi_backup/scripts. To backup a Raspberry Pi that I want to identify as myRPI at address 192.168.0.34. I repeat this until I am happy with the behaviour, including checking for reporting when the RPI is offline.
/volume1/rpi_backup/scripts/backup_rpi.sh myRPI 192.168.0.34

Step 3 Automating the backup

On the DiskStation desktop open Control Panel->Task Scheduler
Create a User Defined Script
Give the task a name and enter the command to run your script with the full path to the script, this example is for my jenkins server at address 192.168.0.34
Set the schedule, in my case I want this task to run at 3am every morning
Save the job by clicking OK
You can now test run the job by selecting it and clicking Run.

Step 4 Restoring from backup

The backup directories are shared from the DiskStation, mounting the volume gives access to the files, for now I have only done this from my mac and used it to restore individual files.
At this stage I have not investigated a full system restore, I rely on having a base img for each RPi taken after any major reconfiguration. I can then restore files from these backups.

Step 5 Backing up a second RPI

We need the Synology DiskStation to have root access via SSH to the Raspberry Pi. Open up two SSH sessions, one to the DiskStation (as root) and one to the Pi (as your normal login e.g. pi).
1) In the Raspberry Pi Session first enable root login by setting a root password

sudo passwd root

Enter a password as prompted
Now we need to ensure that there is a directory to hold ssh keys.

su root

mkdir ~/.ssh

exit

2) In DiskStation session

Push the public key to the Raspberry Pi (this is why we need the Pi to briefly allow password logins on the root account, because until the public key exists on the destination server, you will be prompted for a password when you run this command):

cat ~/.ssh/id_rsa.pub | ssh root@<your Pi’s IP address> ‘cat >> .ssh/authorized_keys’

Enter the password you created in Step 1 when prompted

Confirm that SSH key logins are now accepted by the Raspberry Pi by running this command (still from the Synology session, don’t toggle back to the Pi session yet🙂

ssh root@<Your Pi’s IP Address>

Now you can close the Synology session’s terminal window
 
3) In the Raspberry Pi Session remove password based root access. 

passwd -d root

Be very careful to do this on the Raspberry Pi – you could cause all sorts of problems doing this from another one of your hosts.
Now close down this ssh session as well.

4) Finally repeat step 3 Automating the Backup for the new host.

ToDo

This process has given me a fully automated backup of my Raspery Pi systems. There are a number of improvements that I would like to make as and when time allows.
  1. There is currently no notification sent if the backup fails. For now I inspect the logs, and the backup does at least preserve the last three good backups. It is possible to use the synology notification system, see http://www.beatificabytes.be/wordpress/send-custom-email-notifications-from-scripts-running-on-a-synology/ I may get around to this, I’m hoping that sinology may improve the system though…
  2. I want to investigate if I can perform a full system restore on to a blank card. For now I rely on taking occasional card images, and then this automated system gives me backup of all files.
  3. The timestamp on the individual backup top level directory is incorrect, it gets updated for each backup each time a backup occurs (mv is incorrectly setting the date). I did have a brief look at this, there are a number of obstacles to overcome. mv doesn’t preserve the timestamp on move, I cannot use touch with a specified timestamp because the version available on the DiskStation doesn’t support the feature. I could solve the problem with python, but this isn’t installed as standard. I think I could solve the issue using php, but I haven’t put the time in yet – more important to have a working backup.


Posted in Raspberry Pi and tagged , , .

One Comment

  1. An update to my diskstation, I’m not sure which version has made a few changes:

    1) In the script the path to rsync has changed, this should now read
    RSYNC=/usr/syno/bin/rsync

    2) When setting up the Task Scheduler it is possible to configure it to send an email when run or on failure.

Leave a Reply

Your email address will not be published. Required fields are marked *