rsyncshot creates space-efficient backups using rsync and hard links. Each snapshot looks like a full backup, but unchanged files share disk space across all snapshots.
Supports backing up to local drives (USB, NFS) or remote servers via SSH.
Inspired by Mike Rubel’s rsync snapshots.
# Download and install
wget https://raw.githubusercontent.com/cjennings/rsyncshot/main/rsyncshot
sudo bash ./rsyncshot setup
# Or clone and install
git clone https://github.com/cjennings/rsyncshot
cd rsyncshot
make deps # Install dependencies (auto-detects package manager)
sudo make installAfter setup, edit /etc/rsyncshot/config to set your backup destination.
| Command | Description |
|---|---|
rsyncshot backup | Run an immediate one-off backup |
rsyncshot <name> <count> | Create snapshot with name and retention |
rsyncshot setup | Install script and configure cron jobs |
rsyncshot status | Show installation and environment status |
rsyncshot list | Show existing snapshots with sizes |
rsyncshot dryrun <n> <c> | Preview backup without making changes |
rsyncshot help | Show help message |
# Immediate backup (creates manual.0)
sudo rsyncshot backup
# Keep 24 hourly snapshots
sudo rsyncshot hourly 24
# Keep 7 daily snapshots
sudo rsyncshot daily 7
# Preview what would be backed up
sudo rsyncshot dryrun manual 1
# Check if everything is configured correctly
sudo rsyncshot status
# See existing snapshots
sudo rsyncshot listAll settings live in /etc/rsyncshot/config. Created automatically by rsyncshot setup.
Back up to a remote server over SSH:
REMOTE_HOST="myserver"
REMOTE_PATH="/mnt/backups"Backups go to myserver:/mnt/backups/hostname/.
If your SSH key isn’t in root’s ~/.ssh/, specify the path:
SSH_IDENTITY_FILE="/home/youruser/.ssh/id_ed25519"Back up to a mounted drive (USB, NFS, etc.):
REMOTE_HOST=""
MOUNTDIR="/media/backup"Backups go to /media/backup/hostname/. If the drive isn’t mounted, rsyncshot will try to mount it.
Edit /etc/rsyncshot/include.txt - one directory per line (supports paths with spaces):
/home /etc /usr/local/bin # Comments start with #
Edit /etc/rsyncshot/exclude.txt - one pattern per line:
# Caches and temp files .cache *.tmp *.log # Build artifacts node_modules __pycache__ *.pyc
Setup installs a default cron schedule:
| Type | Schedule | Retention |
|---|---|---|
| Hourly | Every hour, 1am-11pm | 22 |
| Daily | Noon, Monday-Saturday | 6 |
| Weekly | Noon, Sunday | 51 |
Edit with sudo crontab -e.
- rsync copies your directories to
destination/latest/ - Oldest snapshot beyond retention count is deleted
- Existing snapshots rotate (
hourly.0→hourly.1→hourly.2…) latest/is hard-linked tohourly.0(or whatever type you specified)
Hard links mean unchanged files share disk space. A 100GB backup with 24 hourly snapshots might only use 110GB total if most files don’t change.
- Separate by hostname - one drive can back up multiple machines
- Lockfile - prevents overlapping runs
- Validates sources - checks directories exist before starting
- Validates destination - checks mount or SSH connectivity
- Checks rsync exit code - won’t rotate if backup failed
- Read-only snapshots - prevents accidental deletion
- Timestamped logging - all runs logged to
/var/log/rsyncshot.log
- bash
- rsync
- cron
- grep
- flock
- ssh (for remote mode)
The script checks for rsync and ssh at startup and shows install instructions if missing.
# If you cloned the repo
sudo make uninstall
# Or manually
sudo rm /usr/local/bin/rsyncshot
sudo rm -rf /etc/rsyncshot
sudo rm /var/log/rsyncshot.log # optional
sudo crontab -e # remove rsyncshot entriesRun make to see all available targets:
| Target | Description |
|---|---|
| help | Show all targets (default) |
| install | Install rsyncshot and configure cron |
| uninstall | Remove rsyncshot and config |
| deps | Install dependencies (auto-detects distro) |
| lint | Run shellcheck static analysis |
| check | Run lint + full test suite |
| test | Run full test suite |
| test-quick | Run quick tests (skip backup/cron) |
| test-verbose | Run tests with verbose output |
An automated test suite is included in the tests/ directory.
# Run all tests (requires sudo)
sudo ./tests/test_rsyncshot.sh
# Skip slow backup/cron tests
sudo ./tests/test_rsyncshot.sh --quick
# Verbose output
sudo ./tests/test_rsyncshot.sh -v| Category | Tests | Description |
|---|---|---|
| Validation | 6 | Input validation, help command, argument checking |
| Include | 5 | Path parsing, comments, empty lines, spaces in paths |
| Dry-run | 4 | Preview mode doesn’t modify anything |
| Backup | 7 | Directory creation, file copying, rotation, retention, exclusions |
| Cron | 3 | Cron job management, no duplicates, preserves existing jobs |
tests/test_rsyncshot.sh- Main test runnertests/lib/test_helpers.sh- Assertion functions and test environment setuptests/cases/test_validation.sh- Input validation teststests/cases/test_includes.sh- Include file parsing teststests/cases/test_dryrun.sh- Dry-run mode teststests/cases/test_backup.sh- Backup and rotation teststests/cases/test_cron.sh- Cron job management tests
BSD 3-Clause. See LICENSE file.