<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>edsalisbury.net &#187; backup</title>
	<atom:link href="http://edsalisbury.net/tag/backup/feed/" rel="self" type="application/rss+xml" />
	<link>http://edsalisbury.net</link>
	<description>your guide to user-friendly entertainment</description>
	<lastBuildDate>Thu, 01 Dec 2011 22:08:17 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Setting up Differential Backups for Websites</title>
		<link>http://edsalisbury.net/setting-up-differential-backups-for-websites/</link>
		<comments>http://edsalisbury.net/setting-up-differential-backups-for-websites/#comments</comments>
		<pubDate>Mon, 14 Sep 2009 05:46:35 +0000</pubDate>
		<dc:creator>Ed</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[backup]]></category>
		<category><![CDATA[disaster recovery]]></category>
		<category><![CDATA[website]]></category>

		<guid isPermaLink="false">http://www.edsalisbury.net/?p=452</guid>
		<description><![CDATA[<a href="http://edsalisbury.net/setting-up-differential-backups-for-websites/" title="Setting up Differential Backups for Websites"></a>Backups. No one likes doing them, but they&#8217;re too important not to do. Typically when I&#8217;m doing stuff for my websites, I will occasionally do a tar of a directory and save it off, or even set up a script &#8230;<p class="read-more"><a href="http://edsalisbury.net/setting-up-differential-backups-for-websites/">Read more &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<a href="http://edsalisbury.net/setting-up-differential-backups-for-websites/" title="Setting up Differential Backups for Websites"></a><p><a href="http://www.flickr.com/photos/roadhunter/68017710/"><img src="http://www.edsalisbury.net/wp-content/uploads/2009/09/backup-150x144.jpg" alt="Backup Backup Backup by Topato" title="Backup Backup Backup by Topato" width="150" height="144" class="alignright size-thumbnail wp-image-453" /></a><br />
Backups.  No one likes doing them, but they&#8217;re too important not to do.  Typically when I&#8217;m doing stuff for my websites, I will occasionally do a tar of a directory and save it off, or even set up a script that will do this nightly.  Then I have issues with disks filling up, and not wanting to delete my backups for fear of losing something (like the 20 copies aren&#8217;t enough!)<br />
<span id="more-452"></span><br />
Recently, I decided to put an end to my backup worries, and write a differential backup system.  What is a differential backup?  Go go gadget <a href="http://en.wikipedia.org/wiki/Incremental_backup">Wikipedia</a>!  Basically, a differential backup means that I won&#8217;t be filling up my drives any time soon, because I&#8217;m not doing a FULL backup each time &#8212; it&#8217;s only backing up the changes that have been made since the last full backup, which runs weekly and monthly.  I&#8217;ve been running and tuning the script for a while, and wanted to make sure that it&#8217;s working properly.  If you decide to use this script, you <strong>MUST</strong> test the restores with it, as I can&#8217;t be responsible if you lose data and the script somehow fails you.  You have been warned!  Without further ado, the script:</p>
<pre class="brush:perl; gutter: false; wrap-lines: true; ruler: false">
#!/usr/bin/perl
# Website Differential Backup
# Keeps several websites and their databases backed up
# by Ed Salisbury (ed@edsalisbury.net)
# http://www.edsalisbury.net
# (c)2009 Ed Salisbury, Some Rights Reserved
#
# Config File Format
# DIR /path/to/dir
# DB dbname
#
# Limitations:
# * Cannot do differential backups of databases
#
#
# License:
# Except where otherwise noted, this work is licensed under Creative Commons
#   Attribution ShareAlike 3.0.
#
# You are free:
#   * to Share - to copy, distribute and transmit the work
#   * to Remix - to adapt the work
#
# Under the following conditions:
#   * Attribution. You must attribute the work in the manner specified by the
#     author or licensor (but not in any way that suggests that they endorse
#     you or your use of the work).
#   * Share Alike. If you alter, transform, or build upon this work, you may
#     distribute the resulting work only under the same, similar or a
#     compatible license.
#   * For any reuse or distribution, you must make clear to others the license
#     terms of this work. The best way to do this is with a link to the
#     license's web page (http://creativecommons.org/licenses/by-sa/3.0/)
#   * Any of the above conditions can be waived if you get permission from the
#   * Nothing in this license impairs or restricts the author's moral rights.

use strict;
use warnings;

# Place to store backups
my $BACKUP_DIR = &quot;/backup&quot;;
# Configuration file to read what to backup
my $BACKUP_CONF = &quot;/usr/local/etc/backup.conf&quot;;
# Database user/pass
my $DB_USER = 'root';
my $DB_PASS = '**DBPASSWD**';

# Turn off output buffering
$|++;

# Tools
my $TAR = '/bin/tar';
my $GZIP = '/bin/gzip';
my $LS = '/bin/ls';
my $HEAD = '/usr/bin/head';
my $MKDIR = '/bin/mkdir';
my $RM = '/bin/rm';
my $MYSQLDUMP = '/usr/local/mysql/bin/mysqldump';

my @months = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my @days = qw( Sun Mon Tue Wed Thu Fri Sat );
my @dirs;
my @dbs;

# Get current date
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime time;
my $datestamp = sprintf(&quot;%04d%02d%02d&quot;, $year+1900, $mon+1, $mday);
my $day = $days[$wday];

# Read config file
unless (open(CFG, $BACKUP_CONF))
{
    print &quot;Error: Cannot open $BACKUP_CONFn&quot;;
    exit(1);
}
while (&lt;CFG&gt;)
{
    chomp;
    if (/^DIRs+(.*)$/i)
    {
        push (@dirs, $1);
    }
    elsif (/^DBs+(.*)$/i)
    {
        push (@dbs, $1);
    }
}
close (CFG);

# Do Directory Backups
foreach my $dir (@dirs)
{
    print &quot;Backing up $dir... &quot;;

    # Add backslashes for spaces
    $dir =~ s/ /\ /g;

    my $backup_src = $dir;

    # Convert slashes to underscores
    $dir =~ s///_/g;
    # Remove leading slash
    $dir = substr($dir,1);

    unless (-d &quot;$BACKUP_DIR/$dir&quot;)
    {
        system(&quot;$MKDIR -p $BACKUP_DIR/$dir&quot;);
    }

    # Get datestamp of last full backup
    my $last_full = `$LS -t $BACKUP_DIR/$dir/${dir}_full* 2&gt;&amp;1 | $HEAD -1`;
    chomp($last_full);
    if ($last_full =~ /No such file or directory/)
    {
        $last_full = '';
    }
    my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
        $blksize,$blocks) = stat($last_full);

    if ($mday == 1)
    {
        # Monthly Full Backup
        system(&quot;$TAR -cf $BACKUP_DIR/$dir/${dir}_full_monthly_$datestamp.tar $backup_src &gt; /dev/null 2&gt;&amp;1&quot;);
        system(&quot;$GZIP -f $BACKUP_DIR/$dir/${dir}_full_monthly_$datestamp.tar&quot;);
        system(&quot;$RM $BACKUP_DIR/$dir/${dir}_full_weekly_*&quot;);
    }
    elsif ($wday == 0 || !$last_full)
    {
        # Weekly Full Backup
        system(&quot;$TAR -cf $BACKUP_DIR/$dir/${dir}_full_weekly_$datestamp.tar $backup_src  &gt; /dev/null 2&gt;&amp;1&quot;);
        system(&quot;$GZIP -f $BACKUP_DIR/$dir/${dir}_full_weekly_$datestamp.tar&quot;);
    }

    # Daily Differential Backup
    if ($mtime)
    {
        my $newer = sprintf(&quot;%02d-%s&quot;, (localtime($mtime))[3], $months[(localtime($mtime))[4]]);
        system(&quot;$TAR --newer $newer -cf $BACKUP_DIR/$dir/${dir}_diff_daily_$day.tar $backup_src &gt; /dev/null 2&gt;&amp;1&quot;);
    }
    else
    {
        system(&quot;$TAR -cf $BACKUP_DIR/$dir/${dir}_diff_daily_$day.tar $backup_src &gt; /dev/null 2&gt;&amp;1&quot;);
    }
    system(&quot;$GZIP -f $BACKUP_DIR/$dir/${dir}_diff_daily_$day.tar&quot;);

    print &quot;done.n&quot;;
}

# Do Database Backups
foreach my $db (@dbs)
{
    my $db_name = $db;
    $db = &quot;db_$db&quot;;

    unless (-d &quot;$BACKUP_DIR/$db&quot;)
    {
        system(&quot;$MKDIR -p $BACKUP_DIR/$db&quot;);
    }

    print &quot;Backing up $db_name database... &quot;;

    if ($mday == 1)
    {
        # Monthly Backup
        system(&quot;$MYSQLDUMP --user=$DB_USER --password='$DB_PASS' $db_name &gt; $BACKUP_DIR/$db/${db}_monthly_$datestamp.sql&quot;);
        system(&quot;$GZIP -f $BACKUP_DIR/$db/${db}_monthly_$datestamp.sql&quot;);
        system(&quot;$RM $BACKUP_DIR/$db/${db}_weekly_*&quot;);
    }
    elsif ($wday == 0)
    {
        # Weekly Backup
        system(&quot;$MYSQLDUMP --user=$DB_USER --password='$DB_PASS' $db_name &gt; $BACKUP_DIR/$db/${db}_weekly_$datestamp.sql&quot;);
        system(&quot;$GZIP -f $BACKUP_DIR/$db/${db}_weekly_$datestamp.sql&quot;);
    }

    # Daily Backup
    system(&quot;$MYSQLDUMP --user=$DB_USER --password='$DB_PASS' $db_name &gt; $BACKUP_DIR/$db/${db}_daily_$day.sql&quot;);
    system(&quot;$GZIP -f $BACKUP_DIR/$db/${db}_daily_$day.sql&quot;);

    print &quot;done.n&quot;;
}
</pre>
<p>Call the script something like /usr/local/bin/backup, and then create a config file /usr/local/etc/backup.conf, that has the following format:<br />
DIR /www/domain1.com<br />
DIR /www/domain2.com<br />
DB domain1_wp_db<br />
DB domain2_wp_db</p>
<p>(Substituting locations/db names appropriate for your environment of course).   If you use a different config file than /usr/local/etc/backup.conf, be sure to change that line in the script.  Also, you&#8217;ll need to change the mysql root password in the script to be whatever it is for your env (or make it non-root, whichever you want, it just needs to be able to do a mysqldump on the databases)</p>
<p>Next, set up your crontab to run nightly:</p>
<pre>sudo crontab -e</pre>
<p>Add the following line:</p>
<pre>0 1 * * * /usr/local/bin/backup &gt; /dev/null 2&gt;&#038;1</pre>
<p>That should be it!  Again, make sure to do some test restores to verify that it works ok!  Here&#8217;s what a typical backup directory looks like after a couple of months running the script:</p>
<pre>[ed@halo1:/backup/www_edsalisbury.net]$ ls -l
total 144240
-rw-r--r--  1 root root  5423759 Sep 11 01:00 www_edsalisbury.net_diff_daily_Fri.tar.gz
-rw-r--r--  1 root root  1839115 Sep  7 01:00 www_edsalisbury.net_diff_daily_Mon.tar.gz
-rw-r--r--  1 root root  5787136 Sep 12 01:00 www_edsalisbury.net_diff_daily_Sat.tar.gz
-rw-r--r--  1 root root  7193167 Sep 13 01:00 www_edsalisbury.net_diff_daily_Sun.tar.gz
-rw-r--r--  1 root root  3782727 Sep 10 01:00 www_edsalisbury.net_diff_daily_Thu.tar.gz
-rw-r--r--  1 root root  2455973 Sep  8 01:00 www_edsalisbury.net_diff_daily_Tue.tar.gz
-rw-r--r--  1 root root  3268897 Sep  9 01:00 www_edsalisbury.net_diff_daily_Wed.tar.gz
-rw-r--r--  1 root root 23705401 Aug  1 01:00 www_edsalisbury.net_full_monthly_20090801.tar.gz
-rw-r--r--  1 root root 33975458 Sep  1 01:00 www_edsalisbury.net_full_monthly_20090901.tar.gz
-rw-r--r--  1 root root 31453170 Sep  6 01:00 www_edsalisbury.net_full_weekly_20090906.tar.gz
-rw-r--r--  1 root root 28556424 Sep 13 01:00 www_edsalisbury.net_full_weekly_20090913.tar.gz
</pre>
<p>Not too bad, size-wise.  For all of my sites, the backup directory is about 4 GB, and will stay around that size for quite a while.  The differentials get deleted and so do the weekly full backups after the monthly full completes.  This makes it so that the directory only really grows when there&#8217;s a new full monthly.   If you find this script useful or have issues, be sure to post a comment!</p>
]]></content:encoded>
			<wfw:commentRss>http://edsalisbury.net/setting-up-differential-backups-for-websites/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

