Bash Script to download backup from a remote server using CRON and SCP while keeping limited copies.

A simple and easy to download backup from remote servers using CRON and SCP. This example keeps last 4 weekly and 12 monthly backups. Change the values you desire. The way this works is that the remote server creates a zip backup at a specific time (cron) and then 15 minutes later (cron) local server downloads that zip file using SCP.

Before running the commands below in your terminal replace ssh-ed25519 yourlongsshkeygoeshere BackupUser with your own SSH Key (line 26).

Remote Server Setup

Bash commands
## Server Setup
# SSH commands for creating user and download upload directories

groupadd backupusers
useradd -g backupusers -s /sbin/nologin backupuser
mkdir -p /SystemAdmin/BackupStorage/{upload,download}
chown backupuser:backupusers /SystemAdmin/BackupStorage/{upload,download}
chown root:root /SystemAdmin/BackupStorage
chmod 755 /SystemAdmin/BackupStorage

echo "
Match User backupuser
	SSHFile /SystemAdmin/SSH/BackupUser
	ForceCommand internal-sftp
	PasswordAuthentication no
	ChrootDirectory /SystemAdmin/BackupStorage
	PermitTunnel no
	AllowAgentForwarding no
	AllowTcpForwarding no
	X11Forwarding no
" >> /etc/ssh/sshd_config

# Paste your generated SSH key below
mkdir -p /SystemAdmin/SSH
cat > /SystemAdmin/SSH/BackupUser << EOF
ssh-ed25519 yourlongsshkeygoeshere BackupUser
EOF

chmod 0600 /SystemAdmin/SSH/BackupUser
chown -R backupuser:backupusers /SystemAdmin/SSH/BackupUser
systemctl restart sshd

Bash script to find files to backup and zip them, include your desired file extensions to backup. Working dir starts at /var/www, change it to your desired start of working directory and the script will recursively search and include the files in the backup zip file.

/root/Scripts/zip-backup.sh
#!/bin/bash

date=$(date '+%Y-%m-%d %H:%M:%S')
logger -t SystemAdmin -p warning "Zip Backup Job Start"
wdir="/var/www"
backup_dir="/SystemAdmin/BackupStorage/download"
backupfilename="${backup_dir}/Backup.zip"
if [ -e $backupfilename ]
then
rm "$backupfilename"
fi
find "$wdir" -type f '(' -name '*.db' -o -name '*.php' -o -name '*.ini' ')' -exec zip -r "$backupfilename" {} +
echo "Archive created on $date" | zip -z $backupfilename
logger -t SystemAdmin -p warning "Zip Backup Job End"
exit 0

Cron setup. It creates a cron job at 5:30 and calls the zip-backup.sh bash script which zips the files in the archive that you want to include in the backup

30 5 * * * /bin/bash /root/Scripts/zip-backup.sh > /dev/null 2>&1Copy

Local Backup Server Setup

Change the path to /pathtosshkey/id_ed25519_BackupUser your SSH key file

Create two bash files below

/root/SCP-Server1Weekly.sh
#!/bin/bash

FILENAME_PREFIX="/root/Downloads/Server1Weekly"
FILENAME_EXTENSION="zip"
NUM_COPIES_TO_KEEP=4
# Do not use name with any space or special characters
SCRIPT_NAME="[RemoteServer1]"
NL="\n"
LOCKFILE="/var/tmp/${SCRIPT_NAME}Weekly.lock"

TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S)
NEW_FILENAME="${FILENAME_PREFIX}_${TIMESTAMP}.${FILENAME_EXTENSION}"

scp -o ConnectTimeout=30 -o BatchMode=yes -o StrictHostKeyChecking=accept-new -i /pathtosshkey/id_ed25519_BackupUser backupuser@www.remoteserver1.com:/download/Backup.zip ${NEW_FILENAME}
if [ $? -eq 0 ]; then
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Download successful"
log_message+="${NL}${SCRIPT_NAME} Download successful"
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Downloading backup to ${NEW_FILENAME}"
log_message="${SCRIPT_NAME} Downloading backup to ${NEW_FILENAME}"
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Managing old copies"
log_message+="${NL}${SCRIPT_NAME} Managing old copies"
OLD_FILES_TO_DELETE=$(ls -tr "${FILENAME_PREFIX}_"*".${FILENAME_EXTENSION}" | head -n -"${NUM_COPIES_TO_KEEP}")
if [ -n "$OLD_FILES_TO_DELETE" ]; then
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Deleting old copies"
log_message+="${NL}${SCRIPT_NAME} Deleting old copies"
echo "$OLD_FILES_TO_DELETE" | while read -r file; do
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Deleting: $file"
rm "$file"
echo "$file">>"/var/tmp/deleteoldfiles${SCRIPT_NAME}Weekly.txt"
done
IFS=$'\n'
for line in $(cat "/var/tmp/deleteoldfiles${SCRIPT_NAME}Weekly.txt"); do
log_message+="${NL}${SCRIPT_NAME} $line Deleted"
done
rm "/var/tmp/deleteoldfiles${SCRIPT_NAME}Weekly.txt"
else
logger -t SystemAdmin -p warning "${SCRIPT_NAME} No old copies to delete or fewer than ${NUM_COPIES_TO_KEEP} copies present"
log_message+="${NL}${SCRIPT_NAME} No old copies to delete or fewer than ${NUM_COPIES_TO_KEEP} copies present"
fi
if [ -e $LOCKFILE ]
then
rm $LOCKFILE
fi
else
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Download failed"
log_message+="${NL}${SCRIPT_NAME} Download failed"
touch $LOCKFILE
fi
# Notification API if you need one
#generate_post_data()
#{
#cat <<EOF
#{
#"token": "password",
#"title": "$SCRIPT_NAME Backup",
#"message": "$log_message"
#}
#EOF
#}
#curl -i -H "Accept: application/json" -H "Content-Type:application/json" -X POST --data "$(generate_post_data)" "http://example.com/notification-json.php"
exit 0

Monthly backup bash script

/root/SCP-Server1Monthly.sh
#!/bin/bash

FILENAME_PREFIX_WEEKLY="/root/Downloads/Server1Weekly"
FILENAME_PREFIX="/root/Downloads/Server1Monthly"
FILENAME_EXTENSION="zip"
NUM_COPIES_TO_KEEP=12
# Do not use name with any space or special characters
SCRIPT_NAME="[RemoteServer1]"
NL="\n"

TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S)
LOCKFILE="/var/tmp/${SCRIPT_NAME}Weekly.lock"
if [ -e $LOCKFILE ]
then
	logger -t SystemAdmin -p warning "${SCRIPT_NAME} Monthly copy failed because of lock file"
	echo "ERROR: Monthly copy failed because of lock file"
	log_message+="${NL}${SCRIPT_NAME} Monthly copy failed because of lock file"
else

NEW_FILENAME="${FILENAME_PREFIX}_${TIMESTAMP}.${FILENAME_EXTENSION}"

LATEST_FILE_TO_COPY=$(ls -t "${FILENAME_PREFIX_WEEKLY}_"*".${FILENAME_EXTENSION}" | head -n 1)
if [ -n "$LATEST_FILE_TO_COPY" ]; then
cp "$LATEST_FILE_TO_COPY" ${NEW_FILENAME}
if [ $? -eq 0 ]; then
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Monthly copy successful"
log_message+="${NL}${SCRIPT_NAME} Monthly copy successful"
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Monthly backup to ${NEW_FILENAME}"
log_message="${SCRIPT_NAME} Monthly backup to ${NEW_FILENAME}"
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Managing old monthly copies"
log_message+="${NL}${SCRIPT_NAME} Managing old monthly copies"
OLD_FILES_TO_DELETE=$(ls -tr "${FILENAME_PREFIX}_"*".${FILENAME_EXTENSION}" | head -n -"${NUM_COPIES_TO_KEEP}")
if [ -n "$OLD_FILES_TO_DELETE" ]; then
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Deleting old monthly copies"
log_message+="${NL}${SCRIPT_NAME} Deleting old monthly copies"
echo "$OLD_FILES_TO_DELETE" | while read -r file; do
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Deleting: $file"
rm "$file"
echo "$file">>"/var/tmp/deleteoldfiles${SCRIPT_NAME}Monthly.txt"
done
IFS=$'\n'
for line in $(cat "/var/tmp/deleteoldfiles${SCRIPT_NAME}Monthly.txt"); do
log_message+="${NL}${SCRIPT_NAME} $line Deleted"
done
rm "/var/tmp/deleteoldfiles${SCRIPT_NAME}Monthly.txt"
else
logger -t SystemAdmin -p warning "${SCRIPT_NAME} No old copies to delete or fewer than ${NUM_COPIES_TO_KEEP} monthly copies present"
log_message+="${NL}${SCRIPT_NAME} No old copies to delete or fewer than ${NUM_COPIES_TO_KEEP} monthly copies present"
fi
else
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Monthly copy failed"
log_message+="${NL}${SCRIPT_NAME} Monthly copy failed"
fi
else
logger -t SystemAdmin -p warning "${SCRIPT_NAME} Error: Monthly copy failed, file does not exist or is inaccessible."
log_message+="${NL}${SCRIPT_NAME} Error: Monthly copy failed, file does not exist or is inaccessible."
echo "${SCRIPT_NAME} Error: Monthly copy failed, file does not exist or is inaccessible."
fi

# Notification API if you need one
#generate_post_data()
#{
#cat <<EOF
#{
#"token": "password",
#"title": "$SCRIPT_NAME Backup",
#"message": "$log_message"
#}
#EOF
#}
#curl -i -H "Accept: application/json" -H "Content-Type:application/json" -X POST --data "$(generate_post_data)" "http://example.com/notification-json.php"

fi
exit 0

Cron setup for backup server. Create two cron jobs for backup, one weekly / daily and one monthly. It creates a cron job at 5:45, 15 minutes after the remote server made a backup zip file (at 5:30 above). It runs on 1,7,14,21,28 of every month, change it to daily if you want daily backup and change the numbers of backup to keep.

45 5 1,7,14,21,28 * * /bin/bash /root/SCP-Server1Weekly.sh > /dev/null 2>&1Copy

Monthly cron to create last 12 monthly backups.

55 5 1 * * /bin/bash /root/SCP-Server1Monthly.sh > /dev/null 2>&1Copy

Let me know if you have any comments or if there is any error in this guide.

## Disclaimer

The software provided in this repository is for educational and informational purposes only. Under no circumstances shall the author or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages arising out of the use or inability to use this software. The author will not be liable for any special, incidental, consequential or indirect damages due to loss of data or any other reason.