DIY Linux Time Machine

Backups are a pain, but not taking backups can be even more painful. Disk backups and USB hard drives have become a staple of home users and power users alike. Dealing with Linux and UNIX systems there isn’t a pre-built time machine option like there is in Mac OS with Time Machine. Even though there isn’t a built-in option using rsync can provide a very similar experience. Here is an example script that I wrote a long time ago to do backups for my systems in a time machine type style.

Prerequisites – rsync, mutt (to send email) 


#!/bin/bash
# Setup
#
# The configuration is per backup source in the format listed below.
# LIST FORMAT IS SOURCE%DEST%EXCLUDE1,EXCLUDE2...
SOURCEDESTLIST=`cat <<+ /export/photo%/export/bkp/photo% /export/audio%/export/bkp/audio% + ` #Email Address to send report to EMAIL=user@doain.com # Minimum available disk space to begin backup MINSPACEPERCENT=10 # Tmpfile for log MAILLOG=/tmp/backuplog.$$ ################################### cleanup(){ # Clean up backup storage while [ `df -k -P $BACKUPDESTDIR |tail -1| awk '{printf("%d",$4/$2*100)}'` -lt "$MINSPACEPERCENT" ] do if [ `ls -1 $BACKUPDESTDIR| sed '/lost+found/d' |wc -l` -gt 1 ]; then BACKUPTOREMOVE=${BACKUPDESTDIR}/`ls -1 $BACKUPDESTDIR|sort|sed '/lost+found/d'|head -1` logger $0 Cleaning Up Backup Set: $BACKUPSOURCE Removing Backup: ${BACKUPTOREMOVE} echo $0 Cleaning Up Backup Set: $BACKUPSOURCE Removing Backup: ${BACKUPTOREMOVE} >> $MAILLOG
rm -rf ${BACKUPTOREMOVE}
else
logger $0 ERROR $BACKUPDESTDIR : Below MIN Free Space: ${MINSPACEPERCENT}% and Nothing To Clean Up
echo $0 ERROR $BACKUPDESTDIR : Below MIN Free Space: ${MINSPACEPERCENT}% and Nothing To Clean Up >> $MAILLOG
break
fi
done
}
for backuplist in $SOURCEDESTLIST
do
# BACKUP SOURCE (Normally / for full system backup)
BACKUPSOURCE=`echo $backuplist | awk -F% '{print $1}'`
# LOCATION OF BACKUP SNAPSHOTS
BACKUPDESTDIR=`echo $backuplist | awk -F% '{print $2}'`
# DIRECTORIES TO EXCLUDE (SPACE SEPERATED)
EXCLUDESTRING=`echo $backuplist | awk -F% '{print $3}' | sed 's/,/ /g'`
logger $0 Starting Backup Set: $BACKUPSOURCE to: $BACKUPDESTDIR excludeing: $EXCLUDESTRING
echo $0 Starting Backup Set: $BACKUPSOURCE to: $BACKUPDESTDIR excludeing: $EXCLUDESTRING >> $MAILLOG
DATE=`date +%Y-%m-%d-%H-%M-%S`
BACKUPCURRDIR=${BACKUPDESTDIR}/${DATE}
LASTBACKUP="${BACKUPDESTDIR}/`ls -1 $BACKUPDESTDIR | sort | sed '/lost+found/d' | tail -1`"
# CHECK DESTINATION DIR EXISTS
if [ ! -d $BACKUPDESTDIR ]; then
logger $0 Creating Destination Directory: $BACKUPDESTDIR
echo $0 Creating Destination Directory: $BACKUPDESTDIR >> $MAILLOG
mkdir -p $BACKUPDESTDIR
fi
# SETUP EXCLUDE STRING OPTIONS
RSYNCEXCLUDE=""
for EXCLUDEDIR in $EXCLUDESTRING
do
RSYNCEXCLUDE="${RSYNCEXCLUDE} --exclude ${EXCLUDEDIR} "
done
cleanup
# Setup for next backup
logger $0 Creating new backup: $BACKUPCURRDIR for Backup Set: $BACKUPSOURCE
echo $0 Creating new backup: $BACKUPCURRDIR for Backup Set: $BACKUPSOURCE >> $MAILLOG
mkdir $BACKUPCURRDIR
if [ ! -d "$LASTBACKUP" ]; then
# Handle case of no existing backup
rsync -a -x --delete $RSYNCEXCLUDE $BACKUPSOURCE $BACKUPCURRDIR >> $MAILLOG 2>&1
RSYNCEXIT=$?
else
# Create new backup hardlinking to the last backup for unchanged files
rsync -a -x --delete $RSYNCEXCLUDE --link-dest=$LASTBACKUP $BACKUPSOURCE $BACKUPCURRDIR >> $MAILLOG 2>&1
RSYNCEXIT=$?
fi
cleanup
if [ $RSYNCEXIT -ne 0 ]; then
logger $0 ERROR Backup Set: $BACKUPSOURCE rsync exit code was $RSYNCEXIT
echo $0 ERROR Backup Set: $BACKUPSOURCE rsync exit code was $RSYNCEXIT >> $MAILLOG
else
logger $0 SUCCESS Backup Set: $BACKUPSOURCE Completed: $BACKUPCURRDIR
echo $0 SUCCESS Backup Set: $BACKUPSOURCE Completed: $BACKUPCURRDIR >> $MAILLOG
fi
done
if [ `grep -c -i "error" $MAILLOG` -gt 0 ]; then
cat $MAILLOG | mutt -F ~/.muttrc -s "$HOSTNAME BACKUP ERRORS" ${EMAIL}
fi
rm $MAILLOG