Set up automated backup systems with BorgBackup's advanced compression and deduplication alongside rsync strategies for maximum storage efficiency and reliable data protection.
Prerequisites
- Root or sudo access
- At least 2GB free disk space for backups
- Basic understanding of systemd and file permissions
What this solves
Large backup storage costs and slow backup transfers eat into infrastructure budgets and recovery time windows. BorgBackup provides cryptographically secure deduplication that can reduce backup sizes by 80-95% compared to traditional tools, while rsync offers efficient incremental transfers for frequently changing data. This tutorial shows you how to configure both tools with optimal compression settings, automated scheduling via systemd timers, and monitoring integration for production backup workflows.
Step-by-step configuration
Install BorgBackup and rsync
Install the backup tools and dependencies for compression algorithms.
sudo apt update
sudo apt install -y borgbackup rsync lz4 zstd python3-llfuse
Create backup directories and user
Set up dedicated backup storage locations and service user with proper permissions.
sudo mkdir -p /var/backups/borg /var/backups/rsync /var/log/backups
sudo useradd --system --home /var/backups --shell /bin/bash backup
sudo chown -R backup:backup /var/backups
sudo chmod 750 /var/backups /var/log/backups
Initialize BorgBackup repository
Create an encrypted Borg repository with optimal settings for deduplication.
sudo -u backup borg init --encryption=repokey-blake2 /var/backups/borg/main
sudo -u backup borg key export /var/backups/borg/main /var/backups/borg-key-backup.txt
Configure BorgBackup wrapper script
Create an automated backup script with compression and deduplication optimized for your data types.
#!/bin/bash
set -euo pipefail
Configuration
REPO="/var/backups/borg/main"
BACKUP_DIRS="/etc /home /var/www /opt"
LOGFILE="/var/log/backups/borg-$(date +%Y%m%d-%H%M%S).log"
RETENTION_DAILY=7
RETENTION_WEEKLY=4
RETENTION_MONTHLY=6
Export for Borg
export BORG_REPO="$REPO"
export BORG_PASSPHRASE="$(cat /var/backups/.borg-passphrase)"
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
Logging function
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOGFILE"
}
Start backup
log "Starting BorgBackup process"
Create backup with zstd compression (good balance of speed/ratio)
log "Creating archive with compression"
borg create \
--verbose \
--filter AME \
--list \
--stats \
--show-rc \
--compression zstd,3 \
--exclude-caches \
--exclude '/home/*/.cache' \
--exclude '/var/cache' \
--exclude '/var/tmp' \
--exclude '/tmp' \
--exclude '/var/log/backups' \
"::backup-{now}" \
$BACKUP_DIRS 2>&1 | tee -a "$LOGFILE"
Prune old backups
log "Pruning old archives"
borg prune \
--verbose \
--list \
--prefix backup- \
--show-rc \
--keep-daily $RETENTION_DAILY \
--keep-weekly $RETENTION_WEEKLY \
--keep-monthly $RETENTION_MONTHLY 2>&1 | tee -a "$LOGFILE"
Compact repository to reclaim space
log "Compacting repository"
borg compact --verbose 2>&1 | tee -a "$LOGFILE"
Check repository integrity (weekly)
if [ "$(date +%u)" -eq 1 ]; then
log "Running repository check (weekly)"
borg check --verbose --verify-data --show-rc 2>&1 | tee -a "$LOGFILE"
fi
log "BorgBackup completed successfully"
Send metrics to monitoring system (optional)
if command -v curl &> /dev/null && [ -n "${PROMETHEUS_GATEWAY:-}" ]; then
REPO_SIZE=$(borg info --json | jq -r '.repository.total_size')
curl -X POST "$PROMETHEUS_GATEWAY/metrics/job/borgbackup/instance/$(hostname)" \
--data-binary "borgbackup_repo_size_bytes $REPO_SIZE"
fi
Set up BorgBackup passphrase
Create a secure passphrase file with restricted permissions.
sudo -u backup openssl rand -base64 32 > /var/backups/.borg-passphrase
sudo chmod 400 /var/backups/.borg-passphrase
sudo chown backup:backup /var/backups/.borg-passphrase
Make backup script executable
Set proper permissions on the BorgBackup wrapper script.
sudo chmod +x /usr/local/bin/borg-backup.sh
sudo chown backup:backup /usr/local/bin/borg-backup.sh
Configure rsync backup script
Create an rsync script optimized for incremental backups with compression and deduplication through hard links.
#!/bin/bash
set -euo pipefail
Configuration
SOURCE_DIRS=("/var/www" "/home" "/opt")
BACKUP_ROOT="/var/backups/rsync"
DATE=$(date +%Y%m%d-%H%M%S)
CURRENT="$BACKUP_ROOT/current"
INCREMENT="$BACKUP_ROOT/increment-$DATE"
LOGFILE="/var/log/backups/rsync-$(date +%Y%m%d-%H%M%S).log"
RETENTION_DAYS=30
Logging function
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOGFILE"
}
log "Starting rsync incremental backup"
Create backup directories
mkdir -p "$BACKUP_ROOT" "$INCREMENT"
Rsync with hard links for deduplication
for source in "${SOURCE_DIRS[@]}"; do
if [ -d "$source" ]; then
log "Backing up $source"
# Create target directory structure
target="$INCREMENT$(dirname "$source")"
mkdir -p "$target"
# Rsync with hard link deduplication
rsync -avz \
--compress \
--compress-level=6 \
--delete \
--delete-excluded \
--hard-links \
--link-dest="$CURRENT$(dirname "$source")" \
--exclude='*.tmp' \
--exclude='*.log' \
--exclude='.cache/' \
--exclude='lost+found/' \
--stats \
"$source/" "$INCREMENT$source/" 2>&1 | tee -a "$LOGFILE"
else
log "Warning: Source directory $source does not exist, skipping"
fi
done
Update current symlink
log "Updating current backup symlink"
rm -f "$CURRENT"
ln -sf "increment-$DATE" "$CURRENT"
Clean up old backups
log "Cleaning up backups older than $RETENTION_DAYS days"
find "$BACKUP_ROOT" -maxdepth 1 -name "increment-*" -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \;
Calculate storage efficiency
TOTAL_SIZE=$(du -sb "$BACKUP_ROOT" | cut -f1)
log "Total backup storage usage: $(numfmt --to=iec-i --suffix=B "$TOTAL_SIZE")"
log "Rsync backup completed successfully"
Set rsync script permissions
Make the rsync backup script executable with proper ownership.
sudo chmod +x /usr/local/bin/rsync-backup.sh
sudo chown backup:backup /usr/local/bin/rsync-backup.sh
Create systemd timer for BorgBackup
Set up automated scheduling for BorgBackup using systemd timers.
[Unit]
Description=BorgBackup automated backup
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
User=backup
Group=backup
ExecStart=/usr/local/bin/borg-backup.sh
StandardOutput=journal
StandardError=journal
Security hardening
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/var/backups /var/log/backups
ProtectKernelTunables=yes
ProtectControlGroups=yes
RestrictRealtime=yes
MemoryDenyWriteExecute=yes
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
Create systemd timer for rsync
Configure systemd timer for frequent rsync incremental backups.
[Unit]
Description=Rsync incremental backup
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
User=backup
Group=backup
ExecStart=/usr/local/bin/rsync-backup.sh
StandardOutput=journal
StandardError=journal
Security hardening
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/var/backups /var/log/backups
ProtectKernelTunables=yes
ProtectControlGroups=yes
RestrictRealtime=yes
MemoryDenyWriteExecute=yes
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
Configure backup timers
Create timer configurations for regular backup execution.
[Unit]
Description=Run BorgBackup daily
Requires=borgbackup.service
[Timer]
Run daily at 2 AM
OnCalendar=daily
Persistent=true
RandomizedDelaySec=3600
[Install]
WantedBy=timers.target
[Unit]
Description=Run rsync backup every 4 hours
Requires=rsyncbackup.service
[Timer]
Run every 4 hours
OnCalendar=--* 0,4,8,12,16,20:00:00
Persistent=true
RandomizedDelaySec=900
[Install]
WantedBy=timers.target
Enable and start backup timers
Activate the systemd timers for automated backup execution.
sudo systemctl daemon-reload
sudo systemctl enable --now borgbackup.timer
sudo systemctl enable --now rsyncbackup.timer
Configure backup monitoring script
Create a monitoring script to track backup health and storage efficiency.
#!/bin/bash
set -euo pipefail
Configuration
BORG_REPO="/var/backups/borg/main"
RSYNC_ROOT="/var/backups/rsync"
METRICS_FILE="/var/log/backups/metrics.txt"
ALERT_EMAIL="admin@example.com"
MAX_AGE_HOURS=26 # Alert if backup older than 26 hours
Export for Borg
export BORG_REPO="$BORG_REPO"
export BORG_PASSPHRASE="$(cat /var/backups/.borg-passphrase)"
Check BorgBackup status
check_borg() {
echo "=== BorgBackup Status ===" > "$METRICS_FILE"
if borg list --short --last 1 > /dev/null 2>&1; then
LAST_BACKUP=$(borg list --short --last 1 | head -1 | cut -d' ' -f1)
BACKUP_TIME=$(echo "$LAST_BACKUP" | sed 's/backup-//g')
# Get repository info
REPO_INFO=$(borg info --json 2>/dev/null)
TOTAL_SIZE=$(echo "$REPO_INFO" | jq -r '.repository.total_size')
UNIQUE_SIZE=$(echo "$REPO_INFO" | jq -r '.repository.unique_size')
COMPRESSED_SIZE=$(echo "$REPO_INFO" | jq -r '.repository.compressed_size')
# Calculate deduplication ratio
if [ "$TOTAL_SIZE" -gt 0 ]; then
DEDUP_RATIO=$(echo "scale=2; (1 - $UNIQUE_SIZE / $TOTAL_SIZE) * 100" | bc)
else
DEDUP_RATIO=0
fi
# Calculate compression ratio
if [ "$UNIQUE_SIZE" -gt 0 ]; then
COMP_RATIO=$(echo "scale=2; (1 - $COMPRESSED_SIZE / $UNIQUE_SIZE) * 100" | bc)
else
COMP_RATIO=0
fi
echo "Last backup: $LAST_BACKUP" >> "$METRICS_FILE"
echo "Total size: $(numfmt --to=iec-i --suffix=B "$TOTAL_SIZE")" >> "$METRICS_FILE"
echo "Unique size: $(numfmt --to=iec-i --suffix=B "$UNIQUE_SIZE")" >> "$METRICS_FILE"
echo "Compressed size: $(numfmt --to=iec-i --suffix=B "$COMPRESSED_SIZE")" >> "$METRICS_FILE"
echo "Deduplication ratio: ${DEDUP_RATIO}%" >> "$METRICS_FILE"
echo "Compression ratio: ${COMP_RATIO}%" >> "$METRICS_FILE"
# Check backup age
BACKUP_TIMESTAMP=$(date -d "${BACKUP_TIME:0:4}-${BACKUP_TIME:4:2}-${BACKUP_TIME:6:2} ${BACKUP_TIME:9:2}:${BACKUP_TIME:11:2}:${BACKUP_TIME:13:2}" +%s)
CURRENT_TIMESTAMP=$(date +%s)
AGE_HOURS=$(( (CURRENT_TIMESTAMP - BACKUP_TIMESTAMP) / 3600 ))
if [ "$AGE_HOURS" -gt "$MAX_AGE_HOURS" ]; then
echo "WARNING: Last backup is $AGE_HOURS hours old" >> "$METRICS_FILE"
echo "Backup alert: Last BorgBackup is $AGE_HOURS hours old" | mail -s "Backup Alert" "$ALERT_EMAIL" 2>/dev/null || true
fi
else
echo "ERROR: Cannot access BorgBackup repository" >> "$METRICS_FILE"
echo "Backup error: Cannot access BorgBackup repository" | mail -s "Backup Error" "$ALERT_EMAIL" 2>/dev/null || true
fi
}
Check rsync status
check_rsync() {
echo "" >> "$METRICS_FILE"
echo "=== Rsync Backup Status ===" >> "$METRICS_FILE"
if [ -L "$RSYNC_ROOT/current" ]; then
CURRENT_BACKUP=$(readlink "$RSYNC_ROOT/current")
BACKUP_SIZE=$(du -sh "$RSYNC_ROOT/$CURRENT_BACKUP" 2>/dev/null | cut -f1)
TOTAL_BACKUPS=$(ls -1d "$RSYNC_ROOT"/increment-* 2>/dev/null | wc -l)
echo "Current backup: $CURRENT_BACKUP" >> "$METRICS_FILE"
echo "Backup size: $BACKUP_SIZE" >> "$METRICS_FILE"
echo "Total increments: $TOTAL_BACKUPS" >> "$METRICS_FILE"
# Check hard link deduplication efficiency
if command -v hardlink &> /dev/null; then
SAVED_SPACE=$(hardlink -v -n "$RSYNC_ROOT" 2>&1 | grep "saved" | tail -1)
echo "Hard link savings: $SAVED_SPACE" >> "$METRICS_FILE"
fi
else
echo "ERROR: No current rsync backup found" >> "$METRICS_FILE"
fi
}
Run checks
check_borg
check_rsync
Display results
cat "$METRICS_FILE"
echo "Backup monitoring completed at $(date)" >> "$METRICS_FILE"
Set monitoring script permissions
Make the monitoring script executable and set up dependencies.
sudo chmod +x /usr/local/bin/backup-monitor.sh
sudo chown backup:backup /usr/local/bin/backup-monitor.sh
sudo apt install -y bc jq hardlink mailutils
Test initial backups
Run manual backup tests to verify configuration before automation kicks in.
# Test BorgBackup
sudo systemctl start borgbackup.service
sudo systemctl status borgbackup.service
Test rsync backup
sudo systemctl start rsyncbackup.service
sudo systemctl status rsyncbackup.service
Check monitoring
sudo -u backup /usr/local/bin/backup-monitor.sh
Verify your setup
Confirm your backup systems are working and check storage efficiency metrics.
# Check timer status
sudo systemctl list-timers borgbackup rsyncbackup
View BorgBackup archives
sudo -u backup borg list /var/backups/borg/main
Check BorgBackup repository info
sudo -u backup BORG_PASSPHRASE="$(sudo cat /var/backups/.borg-passphrase)" borg info /var/backups/borg/main
View rsync backup structure
sudo ls -la /var/backups/rsync/
Check recent backup logs
sudo ls -la /var/log/backups/
sudo tail -f /var/log/backups/borg-*.log
Run storage efficiency check
sudo -u backup /usr/local/bin/backup-monitor.sh
Configure remote backup storage
For production environments, you should also configure remote storage for your backups. This tutorial shows you how to set up remote backup storage with S3-compatible encryption for off-site protection.
Monitor backup performance
Set up comprehensive backup monitoring with Prometheus and Grafana using our backup monitoring tutorial to track storage efficiency trends and backup health across your infrastructure.
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| BorgBackup "repository not found" error | Repository path incorrect or permissions issue | Check path with sudo -u backup ls -la /var/backups/borg/ and verify ownership |
| High memory usage during backup | Large files or insufficient system memory | Add --chunker-params=19,23,21,4095 to borg create command for large files |
| Rsync permission denied errors | Backup user lacks read access to source directories | Add backup user to necessary groups: sudo usermod -a -G www-data,users backup |
| Poor compression ratios | Already compressed files (media, archives) | Use --compression auto,zstd,3 for adaptive compression |
| Timer not running backups | Service file syntax error or systemd not reloaded | Check with sudo systemctl status borgbackup.service and reload systemd |
| Repository corruption warnings | Hardware issues or interrupted backup | Run sudo -u backup borg check --repair /var/backups/borg/main |
Optimize compression settings
Fine-tune compression algorithms based on your data types:
- Text/logs: Use
zstd,9for maximum compression - Mixed data: Use
zstd,3(default) for balanced performance - Large files: Use
lz4for speed over compression ratio - Pre-compressed: Use
noneto skip compression entirely
Storage efficiency best practices
Next steps
- Setup automated backup verification and recovery testing to ensure your backups are restorable
- Configure backup encryption with GPG and rsync for additional security layers
- Implement MySQL backup automation with Percona XtraBackup for database-specific backup strategies
- Configure remote backup replication with BorgBackup and rsync for disaster recovery
- Setup backup monitoring dashboards with Grafana and Prometheus for operational visibility
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging function
log() {
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"
}
success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1"
}
warn() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
# Usage function
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -d DIRS Backup directories (default: '/etc /home /var/www /opt')"
echo " -p PHRASE Custom passphrase (optional - will generate if not provided)"
echo " -h Show this help message"
exit 1
}
# Cleanup function
cleanup() {
if [ $? -ne 0 ]; then
error "Installation failed. Cleaning up..."
systemctl stop borg-backup.timer 2>/dev/null || true
systemctl disable borg-backup.timer 2>/dev/null || true
userdel backup 2>/dev/null || true
rm -rf /var/backups/borg /etc/systemd/system/borg-backup.* 2>/dev/null || true
systemctl daemon-reload 2>/dev/null || true
fi
}
trap cleanup ERR
# Parse arguments
BACKUP_DIRS="/etc /home /var/www /opt"
CUSTOM_PASSPHRASE=""
while getopts "d:p:h" opt; do
case $opt in
d) BACKUP_DIRS="$OPTARG" ;;
p) CUSTOM_PASSPHRASE="$OPTARG" ;;
h) usage ;;
*) usage ;;
esac
done
# Check if running as root
if [ "$EUID" -ne 0 ]; then
error "This script must be run as root"
exit 1
fi
log "Starting BorgBackup installation and configuration..."
# Detect distribution
echo "[1/10] Detecting Linux distribution..."
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
# Check if dnf is available, fall back to yum
if ! command -v dnf &> /dev/null; then
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
fi
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
;;
*)
error "Unsupported distribution: $ID"
exit 1
;;
esac
success "Detected $PRETTY_NAME"
else
error "Cannot detect Linux distribution"
exit 1
fi
# Update package manager
echo "[2/10] Updating package manager..."
$PKG_UPDATE
# Install packages
echo "[3/10] Installing BorgBackup and dependencies..."
case "$PKG_MGR" in
apt)
$PKG_INSTALL borgbackup rsync lz4 zstd python3-llfuse jq curl openssl
;;
dnf|yum)
$PKG_INSTALL borgbackup rsync lz4 zstd python3-llfuse jq curl openssl
# Enable EPEL if needed for older RHEL-based systems
if ! rpm -q epel-release &>/dev/null && [ "$ID" != "fedora" ]; then
$PKG_INSTALL epel-release 2>/dev/null || warn "Could not install EPEL repository"
fi
;;
esac
success "Packages installed successfully"
# Create backup user and directories
echo "[4/10] Creating backup user and directories..."
if ! id backup &>/dev/null; then
useradd --system --home /var/backups --shell /bin/bash backup
success "Created backup user"
else
warn "Backup user already exists"
fi
mkdir -p /var/backups/borg /var/backups/rsync /var/log/backups
chown -R backup:backup /var/backups /var/log/backups
chmod 750 /var/backups /var/log/backups
success "Created backup directories with proper permissions"
# Generate or set passphrase
echo "[5/10] Setting up repository passphrase..."
if [ -n "$CUSTOM_PASSPHRASE" ]; then
echo "$CUSTOM_PASSPHRASE" > /var/backups/.borg-passphrase
else
openssl rand -base64 32 > /var/backups/.borg-passphrase
fi
chmod 400 /var/backups/.borg-passphrase
chown backup:backup /var/backups/.borg-passphrase
success "Passphrase configured"
# Initialize Borg repository
echo "[6/10] Initializing BorgBackup repository..."
if [ ! -d /var/backups/borg/main ]; then
sudo -u backup bash -c 'export BORG_PASSPHRASE="$(cat /var/backups/.borg-passphrase)"; borg init --encryption=repokey-blake2 /var/backups/borg/main'
sudo -u backup bash -c 'export BORG_PASSPHRASE="$(cat /var/backups/.borg-passphrase)"; borg key export /var/backups/borg/main /var/backups/borg-key-backup.txt'
chmod 400 /var/backups/borg-key-backup.txt
chown backup:backup /var/backups/borg-key-backup.txt
success "Repository initialized"
else
warn "Repository already exists"
fi
# Create backup script
echo "[7/10] Creating backup script..."
cat > /usr/local/bin/borg-backup.sh << 'EOF'
#!/bin/bash
set -euo pipefail
# Configuration
REPO="/var/backups/borg/main"
BACKUP_DIRS="${BACKUP_DIRS:-/etc /home /var/www /opt}"
LOGFILE="/var/log/backups/borg-$(date +%Y%m%d-%H%M%S).log"
RETENTION_DAILY=7
RETENTION_WEEKLY=4
RETENTION_MONTHLY=6
# Export for Borg
export BORG_REPO="$REPO"
export BORG_PASSPHRASE="$(cat /var/backups/.borg-passphrase)"
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
# Logging function
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOGFILE"
}
# Start backup
log "Starting BorgBackup process"
# Create backup with zstd compression
log "Creating archive with compression"
borg create \
--verbose \
--filter AME \
--list \
--stats \
--show-rc \
--compression zstd,3 \
--exclude-caches \
--exclude '/home/*/.cache' \
--exclude '/var/cache' \
--exclude '/var/tmp' \
--exclude '/tmp' \
--exclude '/var/log/backups' \
--exclude '/proc' \
--exclude '/sys' \
--exclude '/dev' \
--exclude '/run' \
"::backup-{now}" \
$BACKUP_DIRS 2>&1 | tee -a "$LOGFILE"
# Prune old backups
log "Pruning old archives"
borg prune \
--verbose \
--list \
--prefix backup- \
--show-rc \
--keep-daily $RETENTION_DAILY \
--keep-weekly $RETENTION_WEEKLY \
--keep-monthly $RETENTION_MONTHLY 2>&1 | tee -a "$LOGFILE"
# Compact repository
log "Compacting repository"
borg compact --verbose 2>&1 | tee -a "$LOGFILE"
# Weekly integrity check
if [ "$(date +%u)" -eq 1 ]; then
log "Running repository check (weekly)"
borg check --verbose --verify-data --show-rc 2>&1 | tee -a "$LOGFILE"
fi
log "BorgBackup completed successfully"
# Optional monitoring integration
if command -v curl &> /dev/null && [ -n "${PROMETHEUS_GATEWAY:-}" ]; then
REPO_SIZE=$(borg info --json | jq -r '.repository.total_size')
curl -X POST "$PROMETHEUS_GATEWAY/metrics/job/borgbackup/instance/$(hostname)" \
--data-binary "borgbackup_repo_size_bytes $REPO_SIZE"
fi
EOF
# Set backup directories in the script
sed -i "s|BACKUP_DIRS=\".*\"|BACKUP_DIRS=\"$BACKUP_DIRS\"|" /usr/local/bin/borg-backup.sh
chmod 755 /usr/local/bin/borg-backup.sh
chown backup:backup /usr/local/bin/borg-backup.sh
success "Backup script created"
# Create systemd service
echo "[8/10] Creating systemd service and timer..."
cat > /etc/systemd/system/borg-backup.service << EOF
[Unit]
Description=BorgBackup Service
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
User=backup
Group=backup
ExecStart=/usr/local/bin/borg-backup.sh
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/var/backups /var/log/backups
ProtectHome=read-only
NoNewPrivileges=true
EOF
cat > /etc/systemd/system/borg-backup.timer << EOF
[Unit]
Description=Run BorgBackup daily
Requires=borg-backup.service
[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable borg-backup.timer
systemctl start borg-backup.timer
success "Systemd service and timer configured"
# Set up log rotation
echo "[9/10] Configuring log rotation..."
cat > /etc/logrotate.d/borgbackup << EOF
/var/log/backups/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 644 backup backup
}
EOF
success "Log rotation configured"
# Final verification
echo "[10/10] Performing verification checks..."
# Check if backup user exists
if id backup &>/dev/null; then
success "Backup user created"
else
error "Backup user missing"
exit 1
fi
# Check if directories exist with correct permissions
if [ -d /var/backups/borg ] && [ -d /var/log/backups ]; then
success "Backup directories created"
else
error "Backup directories missing"
exit 1
fi
# Check if repository is initialized
if sudo -u backup bash -c 'export BORG_PASSPHRASE="$(cat /var/backups/.borg-passphrase)"; borg info /var/backups/borg/main' &>/dev/null; then
success "Repository accessible"
else
error "Repository not accessible"
exit 1
fi
# Check if timer is active
if systemctl is-enabled borg-backup.timer &>/dev/null; then
success "Systemd timer enabled"
else
error "Systemd timer not enabled"
exit 1
fi
log "BorgBackup installation completed successfully!"
echo
success "Installation Summary:"
echo " • Repository: /var/backups/borg/main"
echo " • Backup directories: $BACKUP_DIRS"
echo " • Log directory: /var/log/backups"
echo " • Backup schedule: Daily with randomized delay"
echo " • Repository key backup: /var/backups/borg-key-backup.txt"
echo
warn "IMPORTANT: Back up the repository key file to a secure location!"
echo " sudo cp /var/backups/borg-key-backup.txt /path/to/secure/location/"
echo
echo "Manual backup: sudo -u backup /usr/local/bin/borg-backup.sh"
echo "Check timer status: systemctl status borg-backup.timer"
echo "View logs: journalctl -u borg-backup.service"
Review the script before running. Execute with: bash install.sh