Table of Contents

Automated backups to Strongspace

This is how I backup my account on a Shared Accelerator over to Strongspace. The basic principle is to create a dump file of each component that isn't directly represented on the filesystem (PostgreSQL databases, for example), and then rsync the whole of /users/home/USERNAME over. I perform the backup once a week, and keep the last eight dump files; of course, you should tailor this depending on how your data is changing.

What I'll do is explain how I create a dump of each component in term (this is purely informational; you can skip ahead to the next section, rsync to Strongspace, if you want). I'll then talk a bit about rsync-ing to Strongspace. Finally, I'll explain how to tie this all together into a shell script called via a cronjob.

One last thing to note is that the options used mean that nothing gets echoed unless there's a problem. This is to do with a personal preference about cronjobs, and will get mentioned in that section.

PostgreSQL

The first thing to do is create the directory where the SQL dumps are going to be stored.

% mkdir -p ~/backup/postgresql/{DBONE,DBTWO,DBTHREE}

Replace DBONE etc. with the names of your databases.

For each database, then, we want to run (replacing USERNAME, DBNAME and DATE):

% pg_dump -h localhost -U USERNAME -cOx -f ~/backup/postgresql/DBNAME/DATE.sql DBNAME

You may want to tweak the options, such as -o to include OIDs; “pg_dump --help” is your friend.

Remove all backups more than eight weeks old:

% find ~/backup/postgresql/DBNAME/*.sql -mtime +56 -exec rm {} \;

MySQL

A similar procedure for MySQL. First, create the directory structure:

% mkdir -p ~/backup/mysql/{DBONE,DBTWO,DBTHREE}

Then, for each database:

% mysqldump --user=USERNAME -p -Q --database DBNAME > ~/backup/mysql/DBNAME/DATE.sql
% find ~/backup/mysql/DBNAME/*.sql -mtime +56 -exec rm {} \;

If you're going to do this for multiple databases, you can convert it into a shell function like this (assuming 'bash' shell):

function mysql_backup
{
    database=$1
    shift
    username=$1
    shift
    password=$1
    shift

    mkdir -p $BACKUPDIR/mysql/$database

    echo "$database : mysqldump"
    output=$BACKUPDIR/mysql/$database/`date +%Y%m%d`.sql.gz
    mysqldump --user=$username --password=$password -Q --database $database | gzip > $output
    ls -lhart $output

    # Delete backups older than 3 days
    echo "$database : deleting old backups"
    find $BACKUPDIR/mysql/$database/*.sql.gz -mtime +3 -exec rm {} \;
    echo
}

Then, call it with:

mysql_backup your_database your_username your_password

Subversion

Subversion repositories are actually stored under your home directory (/users/home/USERNAME/svn/REPOS for the primary domain and /users/home/USERNAME/domains/DOMAINNAME.TLD/svn/REPOS for others). Whilst in theory one could just leave them and then rsync across, the risk is that the repository will be copied in a transitional state (such as mid-commit) and you'll have a faulty backup.

So, let's create a directory structure:

% mkdir -p ~/backup/subversion/{REPOSONE,REPOSTWO,REPOSTHREE}

Then, for each repository (compressing for good measure):1)

% svnadmin dump -q ~/svn/REPOSNAME | gzip > ~/backup/subversion/REPOSNAME/DATE.gz
% find ~/backup/subversion/REPOSNAME/*.gz -mtime +56 -exec rm {} \;

rsync to Strongspace

The first thing to do is to set up passwordless logins to Strongspace. This is done using an authentication key which doesn't have a password on it.

First, let's create the key (press <return> at every prompt without typing anything):

% ssh-keygen -t dsa

Next, upload the public part of the key to ~/.ssh/authorized_files on Strongspace (replace USERNAME and ORG with your Strongspace details). If you've already got a key in authorized_files, then be sure to just append it, because the code below will overwrite it 2).

% cd ~/.ssh
% sftp USERNAME@ORG.strongspace.com
Conecting to ORG.strongspace.com...
Password:
sftp> mkdir .ssh
sftp> put id_dsa.pub authorized_keys

As anyone who can access the private key can login to your Strongspace account without a password, we want to make sure that the permissions on everything are appropriate (it can also stop passwordless logins working correctly if they're not).

% chmod 700 ~/.ssh && chmod 600 ~/.ssh/id_dsa
% sftp USERNAME@ORG.strongspace.com
Conecting to ORG.strongspace.com...
Password:
sftp> chmod 700 .ssh
Changing mode on /home/USERNAME/.ssh
sftp> chmod 600 .ssh/authorized_keys
Changing mode on /home/USERNAME/.ssh/authorized_keys

Verify that the passwordless login now works. You should get an SFTP prompt without being asked for a password:

% sftp USERNAME@ORG.strongspace.com
Connecting to ORG.strongspace.com...
sftp>

Finally, the rsync command that we're using is (note that JOYUSER and SSUSER aren't necessarily the same; replace BACKUPDIR as appropriate—I use cooper, the name of my Shared Accelerator):

% rsync -rltpqz --delete /users/home/JOYUSER/ SSUSER@ORG.strongspace.com:BACKUPDIR

rsync performs an incremental one-way sync, so the first time you run it it could take quite a while as it needs to copy everything over (after that, it only copies changes). It is therefore advisable to run rsync once now before setting up the cronjob (replace -q with -v as well) to avoid a really long first cronjob.

Cronjob script

Having explained a bit about each component, I'm now going to tie that all together by creating a shell script that can be called via a cronjob; it doesn't pre-suppose that you've done any of the above sections except for set up passwordless rsyncs to Strongspace. Note that there are a few changes due to the fact that it's called via cron.

Put this script in ~/backup/backup.sh (nano is oft-cited as an easy-to-use text editor), replacing values in the configuration section as appropriate (replace the bit to the right of the equals sign).

#!/bin/sh

# THIS IS THE CONFIGURATION SECTION ############################################

# The primary username that you use to login to the Shared Accelerator, 
# PostgreSQL and MySQL (i.e. /users/home/username).
JOYUSER=username

# Your PostgreSQL password.  For most people, this is the same as their primary 
# user's password (unless you've deliberately unlinked them via Virtualmin).
PGPASSWORD=password

# A space-separated list of PostgreSQL database names to backup.
PGDBS='psql_dbone psql_dbtwo'

# Your MySQL password.  Once again, this is probably the same as your primary
# user's password.
MYPASSWORD=password

# A space-separated list of MySQL database names to backup.
MYDBS='mysql_dbthree mysql_dbfour'

# A space-separated list of Subversion repositories to backup.  If they're under
# the primary domain, then just specify the name.  Otherwise, specify them as
# domainname.tld/reposname.
SVNREPOS='reposone repostwo domain.com/reposthree anotherdomain.net/reposfour'

# Your Strongspace username.
SSUSER=username

# Your Strongspace 'organisation' name (if you login to 
# me.strongspace.com, then it's "me").
SSORG=org

# The directory under which to store the backup on Strongspace.  I use "cooper",
# the name of my Shared Accelerator.
SSDIR=joyentsabackupdir

# How many days to keep backups for.  For example, if you backup once a week and
# want to keep eight backups on disk, set this to 56.
MTIME=56


# DON'T EDIT ANYTHING BELOW HERE, UNLESS YOU'RE DOING SOMETHING CUSTOM #########

DATESTAMP=`date "+%Y-%m-%d"`
BASEDIR="/users/home/${JOYUSER}/backup"

for db in ${PGDBS}
do
  /usr/bin/mkdir -p ${BASEDIR}/postgresql/${db}
  PGPASSWORD=${PGPASSWORD} /usr/local/bin/pg_dump -h localhost -U ${JOYUSER} -cOx -f ${BASEDIR}/postgresql/${db}/${DATESTAMP}.sql ${db}
  /usr/bin/find ${BASEDIR}/postgresql/${db}/*.sql -mtime +${MTIME} -exec /usr/bin/rm {} \;
done
unset db

for db in ${MYDBS}
do
  /usr/bin/mkdir -p ${BASEDIR}/mysql/${db}
  /usr/local/bin/mysqldump --user=${JOYUSER} --password=${MYPASSWORD} -Q --database ${db} > ${BASEDIR}/mysql/${db}/${DATESTAMP}.sql
  /usr/bin/find ${BASEDIR}/mysql/${db}/*.sql -mtime +${MTIME} -exec /usr/bin/rm {} \;
done

for repos in ${SVNREPOS}
do
  if [ `/usr/bin/echo ${repos} | /usr/bin/grep -c '/'` = '1' ]
  then
    reposdir="/users/home/${JOYUSER}/domains/`/usr/bin/echo ${repos} | /usr/bin/sed -e 's:/:/svn/:'`"
    reposbackupdir="${BASEDIR}/subversion/`/usr/bin/echo ${repos} | /usr/bin/sed -e 's:/:_:'`"
  else
    reposdir="/users/home/${JOYUSER}/svn/${repos}"
    reposbackupdir="${BASEDIR}/subversion/primary_${repos}"
  fi
  mkdir -p ${reposbackupdir}
  /usr/local/bin/svnadmin dump -q ${reposdir} | /usr/bin/gzip > ${reposbackupdir}/${DATESTAMP}.gz
  /usr/bin/find ${reposbackupdir}/*.gz -mtime +${MTIME} -exec /usr/bin/rm {} \;
done

/usr/local/bin/rsync -rltpqz --delete /users/home/${JOYUSER}/ ${SSUSER}@${SSORG}.strongspace.com:${SSDIR}

Next, we want to make the script executable, and visible by no-one else.

% chmod 700 ~/backup/backup.sh

At this stage, check that it runs okay (if all's well, then it shouldn't output a message.

% ~/backup/backup.sh

Finally, add it as a cronjob. To do this, sign in to Virtualmin. Under Webmin Modules, click Scheduled Cron Jobs, and then Create a new scheduled cron job. In the input box labelled Command put ”/users/home/USERNAME/backup/backup.sh” (without the quotes). Finally, set when you want it to execute (I've set mine to 0500 every Monday), hit Create to finish and you're good to go.

It's worth noting, as previously mentioned, that the script doesn't echo anything out unless it encounters a problem, in which case it'll e-mail you. You can test this out by setting one of the passwords to something incorrect, running the cronjob, and then checking your inbox.

1) You can use either “svnadmin hotcopy” or “svnadmin dump” to create a backup (read more about it here). I prefer “svnadmin dump”; if you know enough to have an opinion then you don't need my help implementing it.
2) Download the file:
% scp USERNAME@ORG.strongspace.com:.ssh/authorized_keys ./authorized_keys-ss
Password:
Append the new key:
% cat ~/.ssh/id_dsa.pub >> ./authorized_keys-ss
Re-upload it:
% scp ./authorized_keys-ss USERNAME@ORG.strongspace.com:.ssh/authorized_keys
Password:
% rm ./authorized_keys-ss