Incremental backup in a dozen lines

What is most important for backups? That's right, reproducibility. So let's make a bike on the knee and on the --link-dest



option of rsync . Our bike will not have a complex git-like restic data structure, nor a bunch of backends like duplicity. But we can restore his work from memory, even under stress.







The --link-dest



option allows you to specify the previous backup version on which rsync will put hard links if files have not changed since the last time.







That is, rsync --link-dest=/var/backups/www.1 remote:/var/www /var/backups/www.0



copies only those files from the remote server to the /var/backups/www.0 folder that have changed, but for the rest it will put a hard link in /var/backups/www.1







Now the trick is small: wrap the rsync



call in code that shifts backups by one back and frees up space for a new backup in /var/backups/www.0



, and also removes the last copy of /var/backups/www.9



.







 #  find /var/www/backups/ -maxdepth 1 -type d -name '*.[0-9]'| sort -rn| while read dir do #     this=`expr match "$dir" '.*\([0-9]\)'`; #   1,  ,      10  let next=($this+1)%$10; basedirname=${dir%.[0-9]} if [ $next -eq 0 ] ; then rm -rf $dir else mv $dir $basedirname.$next fi done
      
      





This code will rename /var/backups/www.1



to /var/backups/www.2



and /var/backups/www.0



rename /var/backups/www.1



.







It remains only to run rsync --link-dest=/var/backups/www.1 remote:/var/www /var/backups/www.0



, adding options to taste. So the --delete



option deletes the files in the last copy ( rsync



does not do this by default), the -C



option ignores the .svn



, .git



folders, patch



artifacts and some other common types of temporary files.







Together:







 #!/bin/bash FROM=$1 #  TO=$2 #  LINKTO=--link-dest=$TO/`basename $FROM`.1 #   OPTS="-Ca --delete" #    rsync NUMBER_OF_BACKUPS=10 #     #    ,       # dir.1, dir.2, dir.3     dir.9 find $TO -maxdepth 1 -type d -name '*.[0-9]'| sort -rn| while read dir do this=`expr match "$dir" '.*\([0-9]\)'`; let next=($this+1)%$NUMBER_OF_BACKUPS; basedirname=${dir%.[0-9]} if [ $next -eq 0 ] ; then rm -rf $dir else mv $dir $basedirname.$next fi done #  ,  rsync rsync $OPTS $LINKTO $FROM/ $TO/`basename $FROM.0`
      
      





Outside of the brackets was error handling (see set -e



) and notifications, and the number of copies can be made customizable.







But it works!








All Articles