Overview
Backups are only useful if they happen automatically, do not fill the disk, and actually restore. This project builds all three properties into a small, production-style script: timestamped archives, automatic deletion of old ones, a restore test, and a nightly schedule.
- backup-rotate.sh: archives any folder into ~/backups/<name>/ with a timestamp.
- Rotation that keeps only the newest N archives.
- A restore check that proves the archive matches the original files.
- A cron entry that runs the backup every night and logs the results.
Prerequisites
- Shell scripting, functions and exit codes — lesson 12.
- cron and full paths — lesson 14.
- A folder to back up (the examples use ~/website).
Step 1: The Backup Script
Save this as ~/bin/backup-rotate.sh and make it executable with chmod +x ~/bin/backup-rotate.sh:
#!/bin/bash# backup-rotate.sh: archive a folder and keep only the newest N backupsset -euo pipefail
SOURCE="${1:?Usage: $0 <folder> [keep]}"KEEP="${2:-5}"DEST="$HOME/backups/$(basename "$SOURCE")"STAMP="$(date +%Y-%m-%d_%H-%M-%S)"ARCHIVE="$DEST/$(basename "$SOURCE")_$STAMP.tar.gz"
[ -d "$SOURCE" ] || { echo "Error: '$SOURCE' is not a folder" >&2; exit 2; }mkdir -p "$DEST"
tar -czf "$ARCHIVE" -C "$(dirname "$SOURCE")" "$(basename "$SOURCE")"echo "$(date '+%F %T') created $(basename "$ARCHIVE") ($(du -h "$ARCHIVE" | cut -f1))"
# Delete everything except the newest $KEEP archives.ls -1t "$DEST"/*.tar.gz | tail -n +$((KEEP + 1)) | while read -r old; do rm -- "$old" echo "$(date '+%F %T') removed old backup $(basename "$old")"done${1:?Usage…}stops with a usage message if no folder is given;${2:-5}defaults to keeping 5 archives.- The timestamp uses only letters, digits, dashes and underscores, so file names are safe and sort in date order.
ls -1tlists archives newest first;tail -n +$((KEEP + 1))selects everything after the first KEEP, and the loop deletes those.- Every line of output starts with a date and time, which makes the log easy to read later.
Step 2: First Backup
Back up ~/website, keeping 3 archives:
~/bin/backup-rotate.sh ~/website 3Click Run to see what this code prints.
Step 3: Test the Rotation
Run it three more times, a second apart. On the fourth archive, the oldest one should be removed:
for i in 1 2 3; do sleep 1; ~/bin/backup-rotate.sh ~/website 3; doneClick Run to see what this code prints.
ls -1 ~/backups/websiteClick Run to see what this code prints.
Step 4: Prove the Restore Works
An untested backup is just a hope. Extract the newest archive into a separate folder and compare it with the original using diff -r, which prints nothing when two folders are identical:
mkdir -p ~/restore-testlatest=$(ls -1t ~/backups/website/*.tar.gz | head -n 1)tar -xzf "$latest" -C ~/restore-testdiff -r ~/website ~/restore-test/website && echo "Restore OK: files match"Click Run to see what this code prints.
The script also rejects bad input with a non-zero exit code, which cron and monitoring tools can detect:
~/bin/backup-rotate.sh ~/nopeecho "exit code: $?"Click Run to see what this code prints.
Step 5: Schedule It with cron
Open your crontab with crontab -e and add a line to back up every night at 02:30, keeping a week of archives and appending all output to a log:
crontab -lClick Run to see what this code prints.
The next morning, run tail ~/backups/backup.log. If a cron job fails silently, the log is where you will find out why — usually a missing full path.
Extend This Project
- Copy each new archive to another machine with rsync or scp over SSH (lesson 13) — a backup on the same disk does not survive a disk failure.
- Back up several folders by looping over a list in a small wrapper script.
- Send yourself an email or chat notification when the script exits with an error.
- Add a weekly cron job that runs the restore test automatically.
Summary
You built a dependable backup system: timestamped archives, automatic rotation, a verified restore and a nightly schedule — the same principles used for real servers.