Back when I first was learning how to use Linux as a young graduate student, our systems administrator for the research group's network setup remote file distribution and similar such tasks using rdist. Being the naive and enthusiastic learner that I was, I decided to use rdist as well to move files back and forth from my laptop computer to the research group's server. What an absolute pain, especially if my root home-directory structure changed! It was good to learn...once, until I stumbled upon rsync.
rsync is a snap to use. Instead of copying every file from one location to another each time it's run, as is the case with rdist, rsync updates the changes that are made between files, only when necessary. So backups changed from a 10-20 minute job using rdist to a 30 second to 2 minute job (100 Mbits/s helps as well!) using rsync. The process became a whole lot simpler — and safer — in the process.
Backups
There are two locations that I do backups. Every hour I perform a local backup on a separate partition /backup on my server's hard drive; and I also perform hourly, daily, weekly, and monthly backups on a remote RAID 5 server using NFS, which is mounted at /mnt/nfs/backup on my local server. The advantage of this is that at worst, I lose an hour's worth of work. I also have a running history of my filesystem in case I have to access an older changed or deleted file, or if I've done something stupid (which has happened on more than one occasion). If you are planning on doing something similar, see the NFS page for setup instructions.
My backup scripts amount to very simple rsync commands that are placed in an /etc/cron.[] directory. cron runs jobs through executable files that are placed in these directories at specific times, automating the entire process. I also keep logs of what is occurring, and rotate them on a weekly basis. Let's run through the process:
1.1 Hourly Backups
Hourly backups are performed by creating a file called /etc/cron.hourly/backup-hourly using sudo:
~>
sudo nano /etc/cron.hourly/backup-hourly
Add the following information to the file:#!/bin/sh
# A safe way to do backups...
# Backup contents of /home/[user]/ to /backup/[user]-hourly locally.
rsync -avz --delete /home/[user]/ /backup/[user]-hourly >> /var/log/rsync/backup-hourly-local.log
# Backup contents of /home/[user]/ to /mnt/nfs/backup/[user]-hourly via NFS.
rsync -avz --delete /home/[user]/ /mnt/nfs/backup/[user]-hourly >> /var/log/rsync/backup-hourly.log
The first rsync
command
"rsync -avz
--delete" copies files using archive mode ("-a")to
preserve the time stamps, links, to copy recursively, etc., verbosely ("-v"), and
using compression for faster data transfers ("-z"). The
"--delete"
option deletes files that are located in the backup directory that are
no longer present in the source directory. Next is the source
directory "/home/[user]/".
The trailing "/"
must be present(!), as it means "copy the contents of this
directory", opposed to "copy the directory by name". (See the man
page for details.) Then comes the target directory, which in this
case is on a local partition "/backup/[user]-hourly".
Notice the lack of the trailing "/",
which in this case must not be present(!). (Again, see the man
page for details.) Finally, since we are using verbose mode, we
are going to cat
the output of the command and save it to a log file located in /var/log/rsync/backup-hourly-local.log
(which we will create).# A safe way to do backups...
# Backup contents of /home/[user]/ to /backup/[user]-hourly locally.
rsync -avz --delete /home/[user]/ /backup/[user]-hourly >> /var/log/rsync/backup-hourly-local.log
# Backup contents of /home/[user]/ to /mnt/nfs/backup/[user]-hourly via NFS.
rsync -avz --delete /home/[user]/ /mnt/nfs/backup/[user]-hourly >> /var/log/rsync/backup-hourly.log
The second rsync command does something very similar, but in this case copies the files to a mounted NFS directory, which is located on my college's RAID 5 server (it helps to become friends with the IT guys). Save and exit.
Next, we need to make sure that the file is executable so that when cron tries to access it, the script can be executed:
~>
sudo chmod ugo+x /etc/cron.hourly/backup-hourly
1.2 Daily backups
Hourly backups are performed by creating a file called /etc/cron.daily/backup-daily using sudo:
~>
sudo nano /etc/cron.daily/backup-daily
Add the following information to the file:#!/bin/sh
# A safe way to do backups...
# Backup contents of /home/[user]/ to /mnt/nfs/backup/[user]-daily via NFS.
rsync -avz --delete /home/[user]/ /mnt/nfs/backup/[user]-daily >> /var/log/rsync/backup-daily.log
Save and exit.
Change the permissions:# A safe way to do backups...
# Backup contents of /home/[user]/ to /mnt/nfs/backup/[user]-daily via NFS.
rsync -avz --delete /home/[user]/ /mnt/nfs/backup/[user]-daily >> /var/log/rsync/backup-daily.log
~>
sudo chmod ugo+x /etc/cron.daily/backup-daily
1.3 Weekly backups
Weekly backups are performed by creating a file called /etc/cron.weekly/backup-weekly using sudo:
~>
sudo nano /etc/cron.weekly/backup-weekly
Add the following information to the file:#!/bin/sh
# A safe way to do backups...
# Backup contents of /home/[user]/ to /mnt/nfs/backup/[user]-weekly via NFS.
rsync -avz --delete /home/[user]/ /mnt/nfs/backup/[user]-weekly >> /var/log/rsync/backup-weekly.log
Save and exit.
Change the permissions:# A safe way to do backups...
# Backup contents of /home/[user]/ to /mnt/nfs/backup/[user]-weekly via NFS.
rsync -avz --delete /home/[user]/ /mnt/nfs/backup/[user]-weekly >> /var/log/rsync/backup-weekly.log
~>
sudo chmod ugo+x /etc/cron.weekly/backup-weekly
1.4 Monthly backups
Monthly backups are performed by creating a file called /etc/cron.monthly/backup-monthly using sudo:
~>
sudo nano /etc/cron.monthly/backup-monthly
Add the following information to the file:#!/bin/sh
# A safe way to do backups...
# Backup contents of /home/[user]/ to /mnt/nfs/backup/[user]-monthly via NFS.
rsync -avz --delete /home/[user]/ /mnt/nfs/backup/[user]-monthly >> /var/log/rsync/backup-monthly.log
Save and exit.
Change the permissions:# A safe way to do backups...
# Backup contents of /home/[user]/ to /mnt/nfs/backup/[user]-monthly via NFS.
rsync -avz --delete /home/[user]/ /mnt/nfs/backup/[user]-monthly >> /var/log/rsync/backup-monthly.log
~>
sudo chmod ugo+x /etc/cron.monthly/backup-monthly
2. Create a /var/log/rsync directory:
~>
sudo mkdir /var/log/rsync
so that when the logs are generated, they
will actually be saved where they are supposed to.3. Create a /etc/logrotate.d/backups file for log rotation:
~>
sudo nano /etc/logrotate.d/backups
Place the following information in the
file, which will
perform standard weekly log rotation of the logs to keep a running
history of what is going on:/var/log/rsync/backup-hourly-local.log
{
notifempty
weekly
missingok
rotate 4
}
/var/log/rsync/backup-hourly.log {
notifempty
weekly
missingok
rotate 4
}
/var/log/rsync/backup-daily.log {
notifempty
weekly
missingok
rotate 4
}
/var/log/rsync/backup-weekly.log {
notifempty
weekly
missingok
rotate 4
}
/var/log/rsync/backup-monthly.log {
notifempty
weekly
missingok
rotate 4
}
Save and exit. Backups are
complete. See — it's a snap!notifempty
weekly
missingok
rotate 4
}
/var/log/rsync/backup-hourly.log {
notifempty
weekly
missingok
rotate 4
}
/var/log/rsync/backup-daily.log {
notifempty
weekly
missingok
rotate 4
}
/var/log/rsync/backup-weekly.log {
notifempty
weekly
missingok
rotate 4
}
/var/log/rsync/backup-monthly.log {
notifempty
weekly
missingok
rotate 4
}
rsync Scripts for File Synchronization
Since I do a lot of work on my laptop, and am away from my master file system on this server, I needed to create a simple, but safe and precise way of transferring files back and forth so maintain file synchronization between my server and laptop. As mentioned in the NFS page, my laptop is setup to mount the server's /home and /var/spool/mail directories when an ethernet connection is established at /mnt/nfs/[server]/home and /mnt/nfs/[server]/mail, respectively. When mounted, it is a simple matter to either rsync files from the server to the laptop to keep the laptop /home directory structure current, or to rsync files from the laptop back to the server if I have been doing a lot of remote work to keep the server /home directory structure current. As long as I make sure my laptop is up to date before going on a trip, and rsync it back to the server once I return, my overall file directory structure is always maintained. And because I perform automatic, hourly backups, if I do something really stupid (like transfer files the wrong way), I only — at worst — lose an hour's worth of work. Not bad, not bad at all.
I am by no means a programmer, so this may be kind of cludgy, but it works. First off, I keep the rsync scripts in a ~/linux/rsync directory. Within this directory, there are three additional sub-directories:
~>
ls
~/linux/rsync
bin to_[client] to_[server]
In the ~/linux/rsync/bin
directory, there is a executable single file called "yesno"
that I ripped off from IDL that allows a user to be
asked a
yes/no question when executed. I do this because when I execute
my backup scripts, I want to verify that I am actually using the
correct one.bin to_[client] to_[server]
The contents of the ~/linux/rsync/bin/yesno file are:
#!/bin/sh
#
# $Id: yesno,v 1.4 1998/04/02 23:28:57 beth Exp $
#
# This script asks a y/n question of the user:
#
# $* - The y/n question to ask, without the "(y/n) ?" at the end.
#
# A 0 is echoed for "no", a 1 for "yes".
#
if [ "`echo -n testing123`" = "testing123" ]; then
ECHO_NONL="echo -n"
ECHO_NONL_TAIL=
else
ECHO_NONL=echo
ECHO_NONL_TAIL=\\c
fi
RESP=""
while [ "$RESP" != y -a "$RESP" != n ]
do
$ECHO_NONL "$*? (y/n): $ECHO_NONL_TAIL" > /dev/tty
read RESP
RESP=`echo $RESP | tr '[A-Z]' '[a-z]'`
if [ "$RESP" != y -a "$RESP" != n ]; then
echo " <Please answer y for yes or n for no>" > /dev/tty
fi
done
if [ "$RESP" = y ]; then
echo 1
else
echo 0
fi
exit 0
Make
sure that the file is executable by the local user only (for security
reasons):#
# $Id: yesno,v 1.4 1998/04/02 23:28:57 beth Exp $
#
# This script asks a y/n question of the user:
#
# $* - The y/n question to ask, without the "(y/n) ?" at the end.
#
# A 0 is echoed for "no", a 1 for "yes".
#
if [ "`echo -n testing123`" = "testing123" ]; then
ECHO_NONL="echo -n"
ECHO_NONL_TAIL=
else
ECHO_NONL=echo
ECHO_NONL_TAIL=\\c
fi
RESP=""
while [ "$RESP" != y -a "$RESP" != n ]
do
$ECHO_NONL "$*? (y/n): $ECHO_NONL_TAIL" > /dev/tty
read RESP
RESP=`echo $RESP | tr '[A-Z]' '[a-z]'`
if [ "$RESP" != y -a "$RESP" != n ]; then
echo " <Please answer y for yes or n for no>" > /dev/tty
fi
done
if [ "$RESP" = y ]; then
echo 1
else
echo 0
fi
exit 0
~>
chmod u+x ~/linux/rsync/bin/yesno
Transferring files from
the
Server to the ClientIn the to_[client] directory is an executable script called "backup_[client].sh" (wait!...shouldn't all the executables be in the bin directory? Yeah, yeah, I know — I told you, it's cludgy), and an exclusion file called "exclude_file.txt". When run, the script will verify that the NFS /home directory from the server is mounted on the laptop, and will then proceed to rsync files from the mounted NFS /home directory of the server to the local /home directory on the laptop. So, the purpose of this script is to rsync files from the server to the laptop.
Here are the contents of ~/linux/rsync/to_[client]/backup_client.sh:
#!/bin/sh
# Author: Gregory Kriehn
# Modified From: Brice Burgess - bhb@iceburg.net
# backup.sh -- backup to a local drive using rsync
# Directories to backup. Separate with a space.
SOURCES="/mnt/nfs/[server]/home/[user]/"
# Directory to backup to. This is where your backup(s) will be stored.
# Exclude trailing slash!
TARGET="/home/[user]"
# Your EXCLUDE_FILE tells rsync what NOT to backup. Leave it unchanged
# if you want to backup all files in your SOURCES. If performing a FULL
# SYSTEM BACKUP, ie. Your SOURCES is set to "/", you will need to
# make use of EXCLUDE_FILE. The file should contain directories and
# filenames, one per line.
# An example of a EXCLUDE_FILE would be:
# /proc/
# /tmp/
# /mnt/
# *.SOME_KIND_OF_FILE
EXCLUDE_FILE="./exclude_file.txt"
# Comment out the following line to disable verbose output
VERBOSE="-v"
###########################
echo "
This script will rsync files from the NFS filesystem on
[server] (the server) to /home/[user] on [client] (the target).
Make sure that NFS is mounted prior to running the script!
"
if [ `sh ./../bin/yesno "Execute the rsync command now"` = 1 ]; then
echo ""
echo "Updating files on [client]..."
echo ""
echo "Verifying sources..."
for source in $SOURCES; do
echo " Checking source $source"
if [ ! -x $source ]; then
echo " Error with $source"
echo " Directory either does not exist, or you do not have proper permissions!"
echo " Exiting..."
exit 2
fi
done
echo " Looking at exclude file..."
if [ -f $EXCLUDE_FILE ]; then
EXCLUDE="--exclude-from=$EXCLUDE_FILE"
echo " ...Exclude file $EXCLUDE_FILE found!"
else
echo " ...Exclude file not present!"
fi
echo " ...Sources verified."
echo ""
echo "Verifying target $TARGET..."
if [ ! -x $TARGET ]; then
echo " Directory either does not exist, or you do not have proper permissions!"
echo " Exiting..."
exit 2
fi
echo " ...Target verified."
echo ""
echo "Running rsync..."
for source in $SOURCES; do
rsync $VERBOSE --exclude=$TARGET/ $EXCLUDE -a --delete $source $TARGET > rsync.log
done
fi
exit 0
The script basically asks you to verify
that you are going to be copying files from the server to the laptop
(hence the use of yesno),
and then proceeds to make sure that the source directory is actually
mounted. If not, it exits. Next, it looks at exclude_file.txt,
which allows you to prevent certain files from being rsync'ed
over. This is great, because files and directories like .bash_history,
.bash_logout,
.e/, .ecore/, .eggcups/, .gconf/, .gconfd/, .gnome/, .gnome2/, .gnome_private/, .ICEauthority,
.ssh/,
.Xauthority,
etc. are all specific to the local computer, and should not be
subject to rsync
(this list is by no means complete). Notice that the script even tells
you the form of exclude_file.txt.
Use it as a basis to create your own. Finally, the script
verifies the presence of the target directory, and then if everything
checks out ok, runs rsync
to transfer the files and creates a local log called "rsync.log".# Author: Gregory Kriehn
# Modified From: Brice Burgess - bhb@iceburg.net
# backup.sh -- backup to a local drive using rsync
# Directories to backup. Separate with a space.
SOURCES="/mnt/nfs/[server]/home/[user]/"
# Directory to backup to. This is where your backup(s) will be stored.
# Exclude trailing slash!
TARGET="/home/[user]"
# Your EXCLUDE_FILE tells rsync what NOT to backup. Leave it unchanged
# if you want to backup all files in your SOURCES. If performing a FULL
# SYSTEM BACKUP, ie. Your SOURCES is set to "/", you will need to
# make use of EXCLUDE_FILE. The file should contain directories and
# filenames, one per line.
# An example of a EXCLUDE_FILE would be:
# /proc/
# /tmp/
# /mnt/
# *.SOME_KIND_OF_FILE
EXCLUDE_FILE="./exclude_file.txt"
# Comment out the following line to disable verbose output
VERBOSE="-v"
###########################
echo "
This script will rsync files from the NFS filesystem on
[server] (the server) to /home/[user] on [client] (the target).
Make sure that NFS is mounted prior to running the script!
"
if [ `sh ./../bin/yesno "Execute the rsync command now"` = 1 ]; then
echo ""
echo "Updating files on [client]..."
echo ""
echo "Verifying sources..."
for source in $SOURCES; do
echo " Checking source $source"
if [ ! -x $source ]; then
echo " Error with $source"
echo " Directory either does not exist, or you do not have proper permissions!"
echo " Exiting..."
exit 2
fi
done
echo " Looking at exclude file..."
if [ -f $EXCLUDE_FILE ]; then
EXCLUDE="--exclude-from=$EXCLUDE_FILE"
echo " ...Exclude file $EXCLUDE_FILE found!"
else
echo " ...Exclude file not present!"
fi
echo " ...Sources verified."
echo ""
echo "Verifying target $TARGET..."
if [ ! -x $TARGET ]; then
echo " Directory either does not exist, or you do not have proper permissions!"
echo " Exiting..."
exit 2
fi
echo " ...Target verified."
echo ""
echo "Running rsync..."
for source in $SOURCES; do
rsync $VERBOSE --exclude=$TARGET/ $EXCLUDE -a --delete $source $TARGET > rsync.log
done
fi
exit 0
Make sure that the file is executable by the local user only (for security reasons):
~>
chmod u+x ~/linux/rsync/to_[client]/backup_[client].sh
Transferring
files from the Client to the ServerIn a similar manner, the to_[server] directory has an executable script called "backup_[server].sh" and its own exclude_file.txt file (which may or may not be the same as your other exclude file, depending upon your needs). When run, the script will verify that the NFS /home directory from the server is mounted on the laptop, and will then proceed to rsync files from the local /home directory on the laptop to the mounted NFS /home directory from the server. So, the purpose of this script is to rsync files from the laptop to the server.
Here are the contents of ~/linux/rsync/to_[server]/backup_server.sh:
#!/bin/sh
# Author: Gregory Kriehn
# Modified From: Brice Burgess - bhb@iceburg.net
# backup.sh -- backup to a local drive using rsync
# Directories to backup. Separate with a space.
SOURCES="/home/[user]/"
# Directory to backup to. This is where your backup(s) will be stored.
# Exclude trailing slash!
TARGET="/mnt/nfs/[server]/home/[user]"
# Your EXCLUDE_FILE tells rsync what NOT to backup. Leave it unchanged
# if you want to backup all files in your SOURCES. If performing a FULL
# SYSTEM BACKUP, ie. Your SOURCES is set to "/", you will need to
# make use of EXCLUDE_FILE. The file should contain directories and
# filenames, one per line.
# An example of a EXCLUDE_FILE would be:
# /proc/
# /tmp/
# /mnt/
# *.SOME_KIND_OF_FILE
EXCLUDE_FILE="./exclude_file.txt"
# Comment out the following line to disable verbose output
VERBOSE="-v"
###########################
echo "
This script will rsync files from the local filesystem on
[client] (The Source) to /mnt/nfs/[server]/home/[user] on [server]
(The Target).
Make sure that NFS is mounted prior to running the script!
"
if [ `sh ./../bin/yesno "Execute the rsync command now"` = 1 ]; then
echo ""
echo "Updating files on [server]..."
echo ""
echo "Verifying sources..."
for source in $SOURCES; do
echo " Checking source $source..."
if [ ! -x $source ]; then
echo " Error with $source"
echo " Directory either does not exist, or you do not have proper permissions!"
echo "Exiting..."
exit 2
fi
done
echo " Looking at exclude file..."
if [ -f $EXCLUDE_FILE ]; then
EXCLUDE="--exclude-from=$EXCLUDE_FILE"
echo " ...Exclude file $EXCLUDE_FILE found!"
else
echo " ...Exclude file not present!"
fi
echo " ...Sources verified."
echo ""
echo "Verifying target $TARGET..."
if [ ! -x $TARGET ]; then
echo " Directory either does not exist, or you do not have proper permissions!"
echo "Exiting..."
exit 2
fi
echo " ...Target verified."
echo ""
echo "Running rsync..."
for source in $SOURCES; do
rsync $VERBOSE --exclude=$TARGET/ $EXCLUDE -a --delete $source $TARGET > rsync.log
done
fi
exit 0
Make sure that the file is executable by
the local user only (for security reasons):# Author: Gregory Kriehn
# Modified From: Brice Burgess - bhb@iceburg.net
# backup.sh -- backup to a local drive using rsync
# Directories to backup. Separate with a space.
SOURCES="/home/[user]/"
# Directory to backup to. This is where your backup(s) will be stored.
# Exclude trailing slash!
TARGET="/mnt/nfs/[server]/home/[user]"
# Your EXCLUDE_FILE tells rsync what NOT to backup. Leave it unchanged
# if you want to backup all files in your SOURCES. If performing a FULL
# SYSTEM BACKUP, ie. Your SOURCES is set to "/", you will need to
# make use of EXCLUDE_FILE. The file should contain directories and
# filenames, one per line.
# An example of a EXCLUDE_FILE would be:
# /proc/
# /tmp/
# /mnt/
# *.SOME_KIND_OF_FILE
EXCLUDE_FILE="./exclude_file.txt"
# Comment out the following line to disable verbose output
VERBOSE="-v"
###########################
echo "
This script will rsync files from the local filesystem on
[client] (The Source) to /mnt/nfs/[server]/home/[user] on [server]
(The Target).
Make sure that NFS is mounted prior to running the script!
"
if [ `sh ./../bin/yesno "Execute the rsync command now"` = 1 ]; then
echo ""
echo "Updating files on [server]..."
echo ""
echo "Verifying sources..."
for source in $SOURCES; do
echo " Checking source $source..."
if [ ! -x $source ]; then
echo " Error with $source"
echo " Directory either does not exist, or you do not have proper permissions!"
echo "Exiting..."
exit 2
fi
done
echo " Looking at exclude file..."
if [ -f $EXCLUDE_FILE ]; then
EXCLUDE="--exclude-from=$EXCLUDE_FILE"
echo " ...Exclude file $EXCLUDE_FILE found!"
else
echo " ...Exclude file not present!"
fi
echo " ...Sources verified."
echo ""
echo "Verifying target $TARGET..."
if [ ! -x $TARGET ]; then
echo " Directory either does not exist, or you do not have proper permissions!"
echo "Exiting..."
exit 2
fi
echo " ...Target verified."
echo ""
echo "Running rsync..."
for source in $SOURCES; do
rsync $VERBOSE --exclude=$TARGET/ $EXCLUDE -a --delete $source $TARGET > rsync.log
done
fi
exit 0
~>
chmod u+x ~/linux/rsync/to_[server]/backup_[server].sh
ConclusionIs there an even easier, or more elegant way of doing this? Probably. (And if you have a better way, contact me!) But contrary to what these web pages may (or may not) lead you to believe, I am not a "real" Linux systems administrator, or even a real programmer. I am basically a Linux enthusiast and a hacker, in the "let's just hack the code to get it to work" kind of way (not the "I like to crack other people's computer systems" kind of way). Don't get me wrong — I use Linux because of its technical superiority, especially since I am a Professor teaching Electrical and Computer Engineering. But at the same time, there is only so much time in a 24 hour period, and the main goal here is using Linux to increase my productivity.


