====== 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, [[automated-backups#rsync-to-strongspace|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):((You can use either "svnadmin hotcopy" or "svnadmin dump" to create a backup (read more about it [[http://svnbook.red-bean.com/en/1.4/svn.reposadmin.maint.html|here]]). I prefer "svnadmin dump"; if you know enough to have an opinion then you don't need my help implementing it.)) % 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 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 ((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 )). % 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.