r-chive.sh is a flexible shell script for automating backups from multiple remote servers using rsync. It is designed to be robust, easy to configure, and provides detailed feedback through logs and email reports.
The script's core philosophy is a job-based system. You define a list of backup "jobs," and for each job, you specify a source and a list of exclusion patterns. This provides highly granular control over your backups.
It performs a "mirror" backup for each specified host. Thanks to rsync's --relative (-R) option, the full source path is recreated within a Live directory at the destination. After a successful sync, it can create compressed archives for each backup job and/or space-efficient, point-in-time snapshots at the host level.
- Job-Based Configuration: A clean, powerful system where you define backup jobs and control them from a master list.
- Strict Job Name Validation: Job names are validated to ensure they only contain letters, numbers, and underscores, preventing common configuration errors.
- Comprehensive Rsync Options: Utilizes a robust set of rsync flags (
-aHAXxv --numeric-ids --delete --delete-excluded --partial --inplace) to ensure backups are a true 1:1 mirror. This preserves all critical metadata (hardlinks, ACLs, extended attributes), uses numeric IDs to prevent user/group mismatches, deletes extraneous files at the destination (including newly excluded ones), and supports resuming interrupted transfers. - Path-Preserving Backups: Uses
rsync's relative path feature to automatically replicate the source directory structure at the destination. - Per-Job Exclusions: Easily specify a list of files and directories to exclude for each backup job individually via the config file.
- Global Excludes: A command-line
--excludeparameter allows you to add temporary, global exclusion patterns for a single run. - Efficient SSH Port Pre-check: Automatically checks if the remote SSH port is open once per host, providing faster feedback on connectivity issues.
- Host-Level Snapshots: Creates space-efficient, point-in-time snapshots of an entire host's backup directory using hard links.
- Granular Archiving: Creates a separate, clean
.tar.zstarchive for each individual backup job. - Advanced Logging:
- A clean, high-level global log file (
r-chive.log). - Optional, detailed per-host logs organized into subdirectories (
HOST/DATE.log). - Live Console Logging: When run in an interactive terminal, all log output is streamed to the console in real-time with color-coding for readability (Errors in red, success in green, etc.).
- Monitor Mode (
LOG_VERBOSE=yes): View real-time transfer progress in the per-host log, perfect fortail -f.
- A clean, high-level global log file (
- Flexible Email Reports: Choose between a concise summary report or a summary with a detailed log file as an attachment. The attached log is always clean, even in monitor mode.
- Flexible Disk Usage Reporting: Optionally include the total size of each backup job in the email report. This can be disabled for performance on very large filesystems.
- Selectable
duTool: Choose between the standardducommand or a faster alternative likegdufor disk usage calculation. - Start Notification Emails: Sends an email notification when a backup process for a host begins, providing immediate confirmation that the job has started.
- Improved Error Reporting: Robustly captures and logs the specific
stderroutput from critical commands (rsync,tar,cp), ensuring no error goes unnoticed. Failed jobs include an actionable error message in the global log and email body. - Parallel Backups: Executes backups for all jobs on a single host concurrently, significantly reducing total backup time.
- Efficient Localhost Backups: Intelligently detects
localhostas a target and performs a direct localrsync(without SSH) for maximum efficiency. - Custom SSH Port Support: Specify a custom SSH port directly in the job's source string.
- Flexible Retention Policies: Clean up old archives and snapshots based on time (days) or count.
- Configuration-Specific Locking: A robust lock file mechanism prevents multiple instances using the same configuration from running simultaneously, while allowing parallel execution of different backup configurations.
- Dry Run Mode: A
--dry-runmode allows you to test your configuration safely. - Robust Interrupt Handling: Gracefully terminates all running jobs and performs a clean exit when interrupted (e.g., via
Ctrl+C). - Accurate Error Detection: Automatically scans rsync output for error patterns (permission denied, IO errors, etc.) and reports accurate status even when rsync exit code is 0.
- Enhanced Email Reports: Email subjects now include status icons (✅/❌) with UTF-8 encoding for proper display across all email clients.
If you're backing up system directories like /etc and encounter "Permission denied" errors on extended attributes or sensitive files, here are solutions:
Add this to your config file to skip extended attributes and ACLs that often cause permission errors:
RSYNC_CUSTOM_OPTS="--no-xattrs --no-acls"Exclude files that require root access:
job_name_EXCLUDES="
# Sensitive files
master.passwd
spwd.db
pwd.db
ssh/*_key*
ssl/private/*
*.key
*.pem
"For complete backup without errors, configure passwordless sudo for rsync:
On Remote Server:
# Edit sudoers
visudo
# Add this line (replace 'foo' with your backup user)
foo ALL=(ALL) NOPASSWD: /usr/bin/rsyncCreate rsync wrapper on remote server:
# Create /usr/local/bin/rsync-wrapper
cat > /usr/local/bin/rsync-wrapper << 'EOF'
#!/bin/sh
exec sudo /usr/local/bin/rsync "$@"
EOFchmod 755 /usr/local/bin/rsync-wrapper
Add this to your configuration file. The --rsync-path option specifies the command that will be executed on the remote server instead of the default rsync binary.
RSYNC_CUSTOM_OPTS="--rsync-path='/usr/local/bin/rsync-wrapper' --no-xattrs --no-acls"
By setting --rsync-path to use the wrapper, you instruct rsync to:
- Connect via SSH to remote server as user foo.
- Instead of just running rsync (which would run as user foo and have limited permissions).
- Run sudo /usr/local/bin/rsync on remote server.
Because you already have this sudoers rule on remote server:
foo ALL=(ALL) NOPASSWD: /usr/local/bin/rsync
Combine Solutions 1 & 2 for best results:
# Global options
RSYNC_CUSTOM_OPTS="--no-xattrs --no-acls"
# Per-job exclusions
backup_etc_EXCLUDES="
master.passwd
spwd.db
ssh/*_key*
ssl/private/*
"- On the Backup Server:
rsync,zstd,nc(netcat), a configured MTA (likesendmail), and an SSH client. Optional:gdufor faster disk usage calculation. - On ALL Remote Servers:
rsyncmust be installed.
Configuration is managed via a .conf file passed as an argument to the script.
The script accepts the following command-line arguments:
| Argument | Description |
|---|---|
config_file |
(Required) The path to your configuration file. |
--dry-run |
(Optional) Simulates the backup process without making any actual changes. |
--check-conf |
(Optional) Performs a comprehensive, read-only check of the configuration, connectivity, and paths, then exits. Does not transfer any data. |
--exclude PATTERN |
(Optional) Adds a temporary exclusion pattern that applies to all jobs for this run. Can be used multiple times. e.g. --exclude "*.log". |
- You define a master list of job names in the
BACKUP_JOBSarray. - For each job name, you define its properties using variables prefixed with that name (e.g.,
myjob_SRC,myjob_EXCLUDES). - The script only executes jobs whose names are present in the
BACKUP_JOBSlist.
| Variable | Description |
|---|---|
BACKUP_JOBS |
(Required) A space-separated list of all backup job names you want to activate. Note: Job names must only contain letters, numbers, and underscores (e.g., server1_db, not server-1-db). |
[job_name]_SRC |
(Required) The source for a specific backup job. The [job_name] prefix must match a name in BACKUP_JOBS. - For remote: "user@host:/path/to/source" or "user@host:PORT:/path/to/source". - For local: "localhost:/path/to/source". The script will automatically use a direct local copy. |
[job_name]_EXCLUDES |
(Optional) A multi-line string containing file or directory patterns to exclude for this specific job. One pattern per line. These are passed to rsync's --exclude option. |
BACKUP_NAME |
(Optional) A descriptive name for this backup set (e.g., "Daily", "Weekly"). This name is automatically formatted and included in email subject lines for better context. Defaults to "General". |
BACKUP_DEST |
(Required) The top-level directory on the backup server where host-specific folders (e.g., 10.11.1.121/) will be created. |
SSH_KEY_PATH |
(Optional) Absolute path to the private SSH key. Leave empty to use the default key. |
REPORT_EMAIL |
The destination email address for backup reports. |
REPORT_EMAIL_VERBOSE |
Set to "no" for a short summary email. Set to "yes" to include a detailed log file as an attachment in the email. |
SEND_START_NOTIFICATION |
Set to "yes" to send a notification email when a backup for a host begins. |
REPORT_SHOW_JOB_SIZE |
Set to "yes" to show the total size of each backup job's destination directory in the report. Defaults to "yes". Can be disabled if slow. |
DU_COMMAND |
(Optional) Specify the command to calculate disk usage: "du" (default) or "gdu". If gdu is selected, it must be installed, and the script will fall back to du if it is not found. |
RSYNC_CUSTOM_OPTS |
(Optional) A string of extra rsync command-line options to be added to every job (e.g., "--no-acls --bwlimit=1000"). Useful for advanced tuning or overriding defaults. Tip: Use "--no-xattrs --no-acls" to avoid permission errors when backing up system directories like /etc. |
MAX_ATTACHMENT_SIZE_MB |
The maximum size (in MB) for an email attachment. If a detailed log file exceeds this, it won't be attached, and a warning will be added to the email body. Set to 0 to disable. |
LOG_DIR |
The directory where log files will be stored. |
LOG_PER_HOST |
Set to "yes" to create detailed, date-stamped log files inside a per-host subdirectory (e.g., LOG_DIR/HOST/DATE.log). Highly recommended. |
LOG_VERBOSE |
Set to "yes" to enable Monitor Mode. This adds --progress to rsync, showing real-time file transfer progress in the per-host log. Useful for monitoring large backups with tail -f. This does not affect the content of the email report. |
CREATE_ARCHIVE |
Set to "yes" to enable per-job archive creation. |
ARCHIVE_DEST |
The parent directory for storing all versioned archives. |
ARCHIVE_RETENTION_DAYS |
(Recommended) Deletes archives older than this many days. Takes precedence over ARCHIVE_RETENTION_COUNT. |
ARCHIVE_RETENTION_COUNT |
(Fallback) Keeps the specified number of the most recent archives per job. Used only if ARCHIVE_RETENTION_DAYS is 0. |
CREATE_SNAPSHOT |
Set to "yes" to enable space-efficient, hard-link based snapshots per host. |
SNAPSHOT_RETENTION_COUNT |
Number of snapshots to keep per host. |
There are two primary ways to get r-chive.sh.
Option A: Git Clone (Recommended)
This is the best way to stay up-to-date with the latest features and bug fixes.
git clone https://github.com/alifgufron/r-chive.sh.git
cd r-chive.shOption B: Download a Release
If you prefer a stable version, you can download a tagged release from the project's releases page.
- Go to the releases page (e.g.,
https://github.com/alifgufron/r-chive.sh/releases). - Download the
.tar.gzor.zipfile for the desired version. - Extract the archive.
Copy backup.conf.sample to a permanent location (e.g., /usr/local/etc/r-chive/main.conf) and edit it. Define your jobs in BACKUP_JOBS and create the corresponding _SRC and _EXCLUDES variables.
Example main.conf:
Basic Configuration (Web Server + System Directory):
# Activate jobs
BACKUP_JOBS="web_server_www system_etc"
# Job 1: Backup website files
web_server_www_SRC="backup_user@web-server-01.example.com:/var/www/html"
web_server_www_EXCLUDES="
wp-content/cache/
*.log
tmp/
"
# Job 2: Backup /etc directory (system files)
system_etc_SRC="backup_user@server-01.example.com:/etc"
system_etc_EXCLUDES="
# Skip sensitive files that require root access
master.passwd
spwd.db
pwd.db
ssh/*_key*
ssl/private/*
"
# Global settings
BACKUP_DEST="/mnt/backups"
REPORT_EMAIL="admin@example.com"
LOG_DIR="/var/log/r-chive"
# Skip extended attributes to avoid permission errors
RSYNC_CUSTOM_OPTS="--no-xattrs --no-acls"Ensure the user running the script on the backup server has passwordless SSH access to the remote servers. Use ssh-copy-id for this.
Ensure nc (netcat) is installed on the backup server: sudo pkg install netcat (FreeBSD) or sudo apt install netcat-traditional (Debian/Ubuntu).
Always perform a dry run first to verify your configuration and connections. You can also add temporary excludes.
# As the user who owns the SSH keys:
./r-chive.sh /usr/local/etc/r-chive/main.conf --dry-run --exclude "*.tmp"Once satisfied, run it for real:
./r-chive.sh /usr/local/etc/r-chive/main.confEdit the crontab for the user that runs the backups (crontab -e).
# Run backup every day at 2:30 AM
30 2 * * * /path/to/r-chive.sh /usr/local/etc/r-chive/main.conf >/dev/null 2>&1The new logging system is designed to work with standard system tools.
- Global Log: The script appends to
/var/log/r-chive/r-chive.log. You should configure your system's log rotation tool (e.g.,newsyslog.confon FreeBSD,logrotateon Linux) to manage this file. - Per-Host Logs: If
LOG_PER_HOSTisyes, detailed logs are created daily (e.g.,/var/log/r-chive/server1/2025-10-05.log). You can use afindcommand in a weekly cron job to clean up old logs, for example:# Deletes host-specific logs older than 30 days find /var/log/r-chive -type f -name "*.log" -mtime +30 -delete
Email subjects now include status icons for quick visual identification:
Success:
✅ [Backup Daily Finished] Report for server1 - From backup-host - Status: SUCCESS
Error Detected:
❌ [Backup Daily Finished] Report for server1 - From backup-host - Status: ERROR
Start Notification:
🚀 [Backup Daily Started] For Host: server1 - From backup-host
The email body includes:
- Overall Status - Shows ✅ SUCCESS or ❌ ERROR for the entire host backup
- Job Details - Each job shows its individual status
- Error Details - If a job fails, detected errors are listed:
❌ Job: backup_etc (user@host:/etc) Status: FAILED (Code: 0) Error Detail: Detected Issues: - Permission denied encountered - IO error encountered - Some files/attributes were not transferred
The script automatically scans rsync output for error patterns, even when rsync returns exit code 0. This ensures you're immediately notified of any issues without needing to check attachment logs.
Detected Error Patterns:
- Permission denied
- Operation failed
- IO error
- Directory access failed (opendir failed)
- Skipping file deletion
- Some files/attributes were not transferred
- Connection reset by peer
- Broken pipe
- Unexpected server error
- Rsync error reported