Data Back-up Bash Script

Data Back-up Requirement

Last year (2008), when getting processes in place for our new web and software development business, getpepper, I put together an all important data backup procedure. My aim was, in the worse case, to ensure that we could restore all but the last few days-worth of data for our own systems and that of our clients.

We mostly use open source systems and tools to create and manage our websites and write our software, with the Ubuntu Linux distribution forming the platform upon which our server-based systems (source control (Subversion), issue tracking (Trac), CRM, etc.) and some of our desktop systems run.

After some research on Linux-based back-up facilities I settled on using a Bash shell script that would allow us to run a regular back-up cycle using external portable hard disks. The weekly back-up shell script, which saves to USB hard drives, is based upon a script provided by Mike Rubel.

The Script

The script uses the rsync tool to provide the incremental functionality of the back-up. Here’s the adapted script that we use.

# ============================
# Author: Paul Pepper (though see description below for credits)
# Created: 6 November 2008
# Description:
# Rotating-snapshot utility adapted from Mike Rubel's make_snapshop.sh which
# can be found at http://www.mikerubel.org/computers/rsync_snapshots/
# Basically, this script performs rotating backup-snapshots of /home whenever
# it is called.
# ============================

#!/bin/bash

unset PATH # suggestion from H. Milz: avoid accidental use of $PATH

# ============================
# System commands used by this script
# ============================
ID=/usr/bin/id
ECHO=/bin/echo

MOUNT=/bin/mount
UMOUNT=/bin/umount
RM=/bin/rm
MV=/bin/mv
CP=/bin/cp
TOUCH=/bin/touch
RSYNC=/usr/bin/rsync

# ============================
# File names and locations
# ============================

MOUNT_DEVICE=/dev/sdb1
MOUNT_POINT=/media/sdb
BACKUP_TO_DIR=/backup/
BACKUP_FROM_DIR=/home
SNAPSHOT=snapshot
EXCLUDES=
BACKUP_TO_PATH=${MOUNT_POINT}/${BACKUP_TO_DIR}/${SNAPSHOT}

# ============================
# The script
# ============================

# Make sure we're running as root
if [ `$ID -u` != '0' ]; then
    $ECHO "$0 must be executed as root. Exiting!"
    exit 1
fi

# Attempt to mount the backup device, else abort
$MOUNT -o rw $MOUNT_DEVICE $MOUNT_POINT
if [ $? -ne 0 ]; then
    $ECHO "$0: Could not mount $MOUNT_DEVICE on $MOUNT_POINT as readwrite"
    exit 1
fi

# Step 1: delete the oldest snapshot, if it exists:
if [ -d ${BACKUP_TO_PATH}.3 ] ; then
    $RM -rf ${BACKUP_TO_PATH}.3
fi

# Step 2: shift the middle snapshots(s) back by one, if they exist
if [ -d ${BACKUP_TO_PATH}.2 ] ; then
    $MV ${BACKUP_TO_PATH}.2 \
    ${BACKUP_TO_PATH}.3
fi

if [ -d ${BACKUP_TO_PATH}.1 ] ; then
    $MV ${BACKUP_TO_PATH}.1 \
    ${BACKUP_TO_PATH}.2
fi

# Step 3: make a hard-link-only (except for dirs) copy of the latest snapshot,
# if that exists
if [ -d ${BACKUP_TO_PATH}.0 ] ; then
    $CP -al ${BACKUP_TO_PATH}.0 \
    ${BACKUP_TO_PATH}.1
fi

# Step 4: rsync from the system into the latest snapshot (notice that
# rsync behaves like cp --remove-destination by default, so the destination
# is unlinked first. If it were not so, this would copy over the other
# snapshot(s) too!
$RSYNC -va --delete --delete-excluded \
    --exclude-from="$EXCLUDES" \
       ${BACKUP_FROM_DIR} ${BACKUP_TO_PATH}.0

# Step 5: update the mtime of our most recent snapshot.
$TOUCH ${BACKUP_TO_PATH}.0

# Unmount the device to which we've written the backup
${UMOUNT} ${MOUNT_POINT}
if [ $? -ne 0 ]; then
    $ECHO "$0: Could not unmount ${MOUNT_POINT}"
    exit 1
fi

Here’s an outline of our weekly back-up process:

1. Grab one of the external hard disks.

2. Attach hard disk to server via a USB connector.

3. Log in to server as a regular user – don’t _su_ to root!

4. Run the back-up shell script:

$ sudo ./backup-snapshot.sh

5. Disconnect hard disk and return to its place of safekeeping!

And that’s it!

All users who perform the back-up must have the necessary permissions to run the shell. I enforce this by adding those users as sudoers, only permitting privileged access to the back-up shell script. Here’s the relevant parts of the sudoers file that grants that access – note that it is recommended that you use visudo when editing this file.

# User alias specification
User_Alias BACKUP_USERS = ann, bill

# Cmnd alias specification
Cmnd_Alias BACKUP_CMND = /somepath/backup-snapshot.sh, /bin/mount, /bin/umount, /bin/touch, /usr/bin/rsync

# Permit BACKUP_USERS to run the back-up script as root from all locations
BACKUP_USERS ALL=(root) BACKUP_CMND