A single-file Bash script for versioned rsync backups, with an interactive menu, cron scheduling, and checksum-based change detection — no dependencies beyond bash, rsync, and cron.
- Menu-driven — add/remove source directories, set a destination, run backups, manage cron, and view logs, all from one interactive menu.
- Checksum-based sync — uses
rsync -cto compare files by checksum rather than size/mtime, so a touched-but-unchanged file is correctly skipped. - Simple version control — before any file is overwritten because its checksum differs, the previous copy is moved into a timestamped
.versions/<run-timestamp>/folder (viarsync --backup --backup-dir), preserving its relative path. Empty version folders from no-op runs are pruned automatically. - Cron integration — install, inspect, or remove a scheduled run (hourly, daily at a set time, every N minutes, or a custom cron expression) directly from the menu.
- Headless mode —
./rsync_backup.sh runperforms a backup non-interactively using the saved config, for use in cron or CI. - Logging — every run appends to
~/.config/rsync_backup/backup.log, including per-directory sync results and skips.
- Bash
rsynccron(only needed if you use the scheduling feature)- macOS or Linux (see Platform notes below)
curl -O https://raw.githubusercontent.com/alexadsett/rsyncbackupscript/main/rsync_backup.sh
chmod +x rsync_backup.sh
Or clone the repo and run it from there directly.
./rsync_backup.sh
1) Add a directory to back up
2) Remove a directory
3) List directories
4) Set destination root
5) Run backup now
6) Install / update cron schedule
7) Show current cron schedule
8) Remove cron schedule
9) View recent log
0) Exit
- Set a destination root (option 4) — e.g. an external drive or mounted backup volume.
- Add source directories (option 1) — each is mirrored under the destination at its own full path (
DEST_ROOT + source path), so multiple sources never collide. - Run a backup (option 5), or install a cron schedule (option 6) to automate it.
./rsync_backup.sh run
Runs a backup using the existing config with no prompts — this is what the cron installer wires up for you automatically, but you can also call it directly from your own scripts or CI.
All state lives under ~/.config/rsync_backup/:
| File | Purpose |
|---|---|
dirs.list |
One source directory per line |
settings.conf |
Stores DEST_ROOT |
backup.log |
Timestamped run history |
Each backup run gets a timestamp, e.g. 2026-07-13_143000. When a file's checksum differs from what's already at the destination, rsync saves the old version to:
<DEST_ROOT>/.versions/<run-timestamp>/<original path>
before overwriting it with the new one. Unchanged files are left alone and produce no version entry. This gives you a lightweight history of every file that ever changed, without a full second copy of unchanged data each run.
Option 6 in the menu offers:
- Every hour
- Every day at a specific time
- Every N minutes
- A custom 5-field cron expression
The job is installed with a marker comment (# rsync_backup_auto_job) so the script can find, replace, or remove only its own entry without touching the rest of your crontab.
- The script uses BSD
sedsyntax (sed -i '') inremove_directory, which is macOS's nativesed. On Linux, change that line tosed -i "${num}d" "$DIRS_FILE"(GNUseddoesn't take the empty backup-suffix argument). - The macOS cron note baked into the installer applies if you're on macOS: grant Terminal (or whatever runs cron) Full Disk Access under System Settings → Privacy & Security, or scheduled runs may silently fail to read source directories.