aboutsummaryrefslogtreecommitdiff

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 install

After 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 list

Configuration

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

  1. rsync copies your directories to destination/latest/
  2. Oldest snapshot beyond retention count is deleted
  3. Existing snapshots rotate (hourly.0hourly.1hourly.2…)
  4. latest/ is hard-linked to hourly.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 entries

Development

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 -v

Test 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 runner
  • tests/lib/test_helpers.sh - Assertion functions and test environment setup
  • tests/cases/test_validation.sh - Input validation tests
  • tests/cases/test_includes.sh - Include file parsing tests
  • tests/cases/test_dryrun.sh - Dry-run mode tests
  • tests/cases/test_backup.sh - Backup and rotation tests
  • tests/cases/test_cron.sh - Cron job management tests

License

BSD 3-Clause. See LICENSE file.