Cover image for post Backup needed

Backup needed

As every good geek I know, that backups are pretty necessary, but never made them. ;) True. The last backup of my local data (mails,...) is about 10 month old and my productive server configuration has even never been updated.

So, because of the recent problems with my workstations, I decided thatn risk is becoming to high and a backup solution is needed. Since my machines have all not so much disk space and backups on the same machine do not really make sense, I defined the following requirenments:

  • Backups have to be saved in several stages (4 backups from the last 4 weeks)

  • Backups have to go onto a remote machine.

  • Individual directories have to be backupable.

  • Disk space should be held as small as possible.

  • Solution has to be so universal to be run on my server and workstation.

This little project gave me the possibility to get a bit deeper into bash scripting. And, do not get me wrong, I found out, bash is a pretty nifty language. Nice. :)

The solution I created is devided into 2 components (shell scripts) of which 1 has to run on the machine to backup (let's call it client here), the other one has to lie on the machine the backup goes to (in this spirit server). The latter one is called by the first one, which is triggered by cron.

Main parts of the scripts are 2 basic Unix/Linux tools: - rsync - cp

I give just a short overview on what the scripts do here, find the complete listings in the extended entry.

The client triggers a local script using cron, which at first connects to the server and rotates the backup spaces the backup the last created backup stage. After that, it call rsync for each directory configured to be backed up, what is pretty easy.

The rotation described is done in a more nifty way. The last recent backup (backup.0/) contains the real files of the backup. When it gets rotated, it is copied away to another directory (backup.1/), but not using a real copy, but let copy create hardlinks (cp -l). This has the effect, that no double disk space is allocated. When rsync overwrites one of the linked files in the backup.0/ dir now, it unlinks it before.

I don't know, if there are better solutions available, but this one is pretty straight forward for me and does everything I want to have in an optimal way. Feel free to leave your comments/enhancements/critics at this entry.

Client part (

#!/bin/bash # ---------------------------------------------------------------------- # # Remote backupscript for PHP Applications webserver # # This script handles to backup important data from the life-srver to a # dedicated backup space on the development server. The backup is done # once a week. # # Author: Tobias Schlitt # Version: 1.0 # # Changelog: # # 7th April 2004 # Initial creation. # # ---------------------------------------------------------------------- # Definition of used system commands ID/usr/bin/id; ECHO/bin/echo; RSYNC/usr/bin/rsync; SSH/usr/bin/ssh; # Definiton of directories to backup declare -a sources sources[0]=/etc; sources[1]=/var/www; # Settings remote_port22 remote_base_dir/backups backup_nameserver # Start backup # This script has to be run as root if (( `$ID -u` != 0 )); then { $ECHO "Backupscript has to be run as root."; exit 1; } fi # Run remote command for backup rotation $SSH "-p $remote_port" "$remote_host" "${remote_base_dir}/" "$backup_name" if [ $? ] then $ECHO "Backup rotation successfull on $remote_host."; else $ECHO "Backup rotation failed on $remote_host."; exit 1 fi for i in "${sources[@]}" do $RSYNC -ae "$SSH -p $remote_port" "--delete" "$i" "${remote_host}:${remote_base_dir}/${backup_name}.0"; if [ $? ] then $ECHO "Successfully backed up $i on $remote_host."; else $ECHO "Backup of $i on $remote_host failed."; exit 1; fi done $ECHO "Finished remote backup successfully at " date "."; exit 0;

Server part (

#!/bin/bash # ---------------------------------------------------------------------- # # Remote part of the remote backup script of PHP Applications. # # This script handles backup rotation on the remote system the backups # get saved on. # # Author: Tobias Schlitt # Version: 1.0 # # Changelog: # # 7th April 2004 # Initial creation. # # ---------------------------------------------------------------------- # Definition of used system commands ID/usr/bin/id; ECHO/bin/echo; CP/bin/cp; MV/bin/mv; RM/bin/rm; MKDIR/bin/mkdir; # Settings rotation_count4 base_dir/backups # Sanity checks if (($rotation_count < 1)) then $ECHO "Rotation count has to be minimally 1!" exit 1; fi if [ ! -d "$base_dir" ] then $ECHO "The base dir does not exist." exit 1; fi if [ ! $1 ] then $ECHO "This script requires the backup name as parameter!" exit 1; fi # $ECHO "Will rotate backup $1 with $rotation_count stages."; # Starting the rotation # Reduce rotation_count cause we start counting by 0 let "rotation_count -= 1"; # Delete last rotation dir, if exists last_dir"${base_dir}/${1}.${rotation_count}" if [ -d "$last_dir" ] then $RM "-rf" "$last_dir" if (($? != 0)) then echo "Could not remove $last_dir"; exit 1; fi fi # Rotate down the other backup dirs while (($rotation_count > 1)) do let "rotation_count -= 1"; actual_dir"${base_dir}/${1}.${rotation_count}"; if [ -d $actual_dir ] then $MV "$actual_dir" "$last_dir"; if (($? != 0)) then $ECHO "Could not move $actual_dir to $last_dir ."; exit 1; fi fi last_dir"$actual_dir"; done # The first dir has to be linked instead of moving let "rotation_count -= 1"; actual_dir"${base_dir}/${1}.${rotation_count}"; if [ -d $actual_dir ] then $CP "-al" "$actual_dir" "$last_dir"; if (($? != 0)) then $ECHO "Could not link $actual_dir to $last_dir ."; exit 1; fi else $MKDIR "$actual_dir"; if (($? != 0)) then $ECHO "Could not create $actual_dir ."; exit 1; fi fi # Rotation successfully finished exit 0;


Give BackupPC a try. Might be an "overkill" for your needs, but if you have a workstation, a notebook, maybe even a server at home etc. it might be useful:

Stefan Neufeind at 2004-04-10