Full system rsync

This script will perform a full system backup with rsync, leaving out /dev and /tmp and what not.

There are several configuration parameters:

  • target : system to back up, must be set to output of uname -n. Used as a sanity check.
  • source_dir : source directory. Set to / for full system backup.
  • target_dir : target directory. Set to a remote directory accessible via rsync over ssh in the form user@host:path.
  • exclude_dir : excluded directories. Set these to temporary directories and other things you don't want backed up.
  • include_dir : included directories. Subfolders of excluded directories to include.
  • enc_mount_dir : where to mount encrypted version of source_dir. Recommended to do this in /tmp somewhere.

This script also requires the encfs password GPG encrypted and stored as $target-encfs-password.gpg. Also, the reverse encfs mount point must be configured before running the script for the first time. To do this, run ENCFS6_CONFIG=“/.encfs6.xml” sudo -E encfs –reverse “source-directory” “temporary-mount-point” and setting up the encryption as desired. When that's done, unmount and run the backup script.

If you want to use ths script for a local hard drive, change flags to

flags="-avP --delete"

and set target_dir to the mountpoint of an external hard drive. Note: make sure that target_dir or a parent is included in exclude_dir otherwise rsync will recurse and your hard drive will get filled up completely.

backup-system

#!/bin/bash
set -f
 
hostname=$(uname -n)
target="my-hostname"
 
source_dir="/"
target_dir="user@server:backup/$target/"
exclude_dir="*/.gvfs /media /run/media /sys /dev /proc /mnt /tmp pagefile.sys hiberfil.sys /home/*/.cache .mozilla/firefox/*/Cache .Trash-1000 /var/cache"
include_dir="/mnt/linux-data/opt /mnt/data"
flags="-avPse ssh --delete --rsync-path=\"rsync --fake-super\""
 
enc_mount_dir="/tmp/encrypted-root-mount/"
 
if [ "0$skip_key" -ne 1 ] ; then
  export key=$(gpg --decrypt "$(pwd)/$target-encfs-password.gpg")
  export skip_key=1
fi
 
# Require root
if [ "$(/usr/bin/id -u)" != "0" ]; then
    sudo -E $0
    exit
fi
 
if [ -n "$target" ] ; then
  if [ "$hostname" != "$target" ] ; then
    echo Error: must be run on $target
    exit 1
  fi
fi
 
include=""
 
# process include directories
for d in $include_dir
do
  found=0
  dir=""
  exc_dir=""
  shortest_parent=""
 
  # Need to deal with rsync idiosyncracies
  # by including excluded directories
  # but not their contents
  # Removes matches to the include directory
  # and finds the shortest parent path
  for d2 in $exclude_dir
  do
    if [[ $d = $d2* ]]; then
      found=1
 
      if [[ -z "$shortest_parent" ]]; then
        shortest_parent=$d2
      else
        if [ ${#shortest_parent} -gt ${#d2} ]; then
          shortest_parent=$d2
        fi
      fi
 
    else
      exc_dir="$exc_dir $d2"
    fi
  done
 
  if [[ $found ]]; then
    exclude_dir=$exc_dir
    path=`dirname "$d"`
    while [[ "$path" != "/" ]]; do
      exclude_dir="$exclude_dir $path/*"
      #include_dir="$include_dir $path"
      include="$include --include $path"
      path=`dirname "$path"`
    done
  fi
 
  include="$include --include $d"
done
 
exclude=""
 
for d in $exclude_dir
do
  exclude="$exclude --exclude $d"
done
 
die () {
    echo >&2 "$@"
    exit 1
}
 
echo "Mounting..."
mkdir -p "$enc_mount_dir"
config_file="/.encfs6.xml"
export ENCFS6_CONFIG="$config_file"
echo $key | sudo -E encfs --reverse --stdinpass "$source_dir" "$enc_mount_dir"
if [ "$?" -ne 0 ] ; then
  echo "Mount error!"
else
  echo "Backing up..."
  dest=`echo "$target_dir" | sed -e 's,\(.\)/$,\1,'`
  dest=${dest}.encfs6.xml
  eval rsync $flags "$config_file" "$dest"
  eval rsync $flags $include $exclude "$enc_mount_dir" "$target_dir"
  echo "Unmounting..."
  fusermount -u "$enc_mount_dir"
fi
rmdir "$enc_mount_dir"
 
echo "Done"