About
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.
Quick Start
# 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.
Commands
| 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 |
Examples
# 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 listConfiguration
All settings live in /etc/rsyncshot/config. Created automatically by rsyncshot setup.
Backup Modes
Remote Mode (SSH)
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"Local Mode
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.
What Gets Backed Up
Edit /etc/rsyncshot/include.txt - one directory per line (supports paths with spaces):
/home
/etc
/usr/local/bin
# Comments start with #
What Gets Excluded
Edit /etc/rsyncshot/exclude.txt - one pattern per line:
# Caches and temp files
.cache
*.tmp
*.log
# Build artifacts
node_modules
__pycache__
*.pyc
Automatic Backups
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.
How It Works
- 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.
Safeguards
- 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
Requirements
- bash
- rsync
- cron
- grep
- flock
- ssh (for remote mode)
The script checks for rsync and ssh at startup and shows install instructions if missing.
Uninstalling
# 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 entriesDevelopment
Run 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 |
Testing
An automated test suite is included in the tests/ directory.
Running Tests
# 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 -vTest Coverage
| 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 |
Test Structure
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
License
BSD 3-Clause. See LICENSE file.
