Configure backup compression and deduplication with BorgBackup and rsync for optimal storage efficiency

Intermediate 25 min Apr 28, 2026 74 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

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
sudo dnf update -y
sudo dnf 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
Important: Store the repository key backup in a secure location outside the server. Without it, your backups are unrecoverable.

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
sudo chmod +x /usr/local/bin/backup-monitor.sh
sudo chown backup:backup /usr/local/bin/backup-monitor.sh
sudo dnf install -y bc jq hardlink mailx

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,9 for maximum compression
  • Mixed data: Use zstd,3 (default) for balanced performance
  • Large files: Use lz4 for speed over compression ratio
  • Pre-compressed: Use none to skip compression entirely

Storage efficiency best practices

Pro tip: BorgBackup's deduplication works at the chunk level, so similar files across different directories will share storage. Rsync's hard links work at the file level, making it ideal for frequent snapshots of mostly unchanged data.

Next steps

Running this in production?

Want this handled for you? Setting up backup compression and deduplication once is straightforward. Keeping it monitored, tuned for changing data patterns, and verified across environments is the harder part. See how we run infrastructure like this for European SaaS and e-commerce teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed cloud infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.