Configure system backup automation with BorgBackup and systemd timers

Intermediate 45 min Apr 13, 2026 790 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up automated, deduplicated backups with BorgBackup and systemd timers for reliable system protection. This tutorial covers installation, repository setup with encryption, automated scheduling, and monitoring for production environments.

Prerequisites

  • Root or sudo access
  • At least 10GB free disk space for backups
  • Basic familiarity with systemd and command line

What this solves

BorgBackup provides deduplicated, encrypted backups that save storage space and protect your data. Combined with systemd timers, you get reliable automated backups that run without cron dependencies and integrate with system logging for better monitoring.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest BorgBackup version.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install BorgBackup

Install BorgBackup from the official repositories. This provides the borg command for creating and managing backup repositories.

sudo apt install -y borgbackup
sudo dnf install -y borgbackup

Create backup directory structure

Create directories for storing backup repositories and scripts. Use proper ownership and permissions for security.

sudo mkdir -p /opt/backups/repos
sudo mkdir -p /opt/backups/scripts
sudo mkdir -p /var/log/backups

Create backup user

Create a dedicated user for running backups. This follows security best practices by avoiding root execution.

sudo useradd --system --home /opt/backups --shell /bin/bash backup-user
sudo chown -R backup-user:backup-user /opt/backups
sudo chown backup-user:backup-user /var/log/backups

Initialize BorgBackup repository

Switch to the backup user and initialize an encrypted repository. The passphrase encrypts all backup data.

sudo -u backup-user bash
cd /opt/backups
export BORG_REPO=/opt/backups/repos/system-backup
export BORG_PASSPHRASE="YourSecurePassphrase123!"
borg init --encryption=repokey-blake2 $BORG_REPO
exit
Note: Store the BORG_PASSPHRASE securely. Without it, your backups are unrecoverable. Consider using a password manager or external key management system.

Create backup script

Create the main backup script that defines what to backup, handles logging, and manages retention policies.

#!/bin/bash

# BorgBackup automation script
# Exit on any error
set -e

# Configuration
export BORG_REPO="/opt/backups/repos/system-backup"
export BORG_PASSPHRASE="YourSecurePassphrase123!"
export BORG_RSH="ssh -i /home/backup-user/.ssh/id_rsa"

# Logging
LOG_FILE="/var/log/backups/system-backup.log"
exec 1> >(tee -a "$LOG_FILE")
exec 2>&1

echo "========== Backup started: $(date) =========="

# Backup paths to include
BACKUP_PATHS="
/etc
/home
/var/log
/opt
/root
/usr/local
"

# Paths to exclude
EXCLUDE_PATTERNS="
--exclude /home/*/.cache
--exclude /home/*/.local/share/Trash
--exclude /var/log/journal
--exclude /var/cache
--exclude /tmp
--exclude '*.tmp'
--exclude '*.log.gz'
--exclude '*.log.[0-9]*'
--exclude 'lost+found'
"

# Create backup with current timestamp
ARCHIVE_NAME="system-{now:%Y-%m-%d_%H:%M:%S}"

echo "Creating archive: $ARCHIVE_NAME"
borg create \
    --verbose \
    --filter AME \
    --list \
    --stats \
    --show-rc \
    --compression lz4 \
    $EXCLUDE_PATTERNS \
    "::$ARCHIVE_NAME" \
    $BACKUP_PATHS

backup_exit=$?

echo "Backup completed with exit code: $backup_exit"

# Prune old backups (retention policy)
echo "Pruning old archives"
borg prune \
    --list \
    --prefix system- \
    --show-rc \
    --keep-daily 7 \
    --keep-weekly 4 \
    --keep-monthly 6 \
    --keep-yearly 1

prune_exit=$?

echo "Prune completed with exit code: $prune_exit"

# Compact repository
echo "Compacting repository"
borg compact

compact_exit=$?

echo "Compact completed with exit code: $compact_exit"

# Global exit status
global_exit=$(($backup_exit > $prune_exit ? $backup_exit : $prune_exit))
global_exit=$(($compact_exit > $global_exit ? $compact_exit : $global_exit))

if [ $global_exit -eq 0 ]; then
    echo "========== Backup completed successfully: $(date) =========="
else
    echo "========== Backup completed with errors: $(date) =========="
fi

echo ""
exit $global_exit

Set script permissions

Make the backup script executable and set secure permissions. Only the backup user should be able to read the script containing the passphrase.

sudo chmod 750 /opt/backups/scripts/system-backup.sh
sudo chown backup-user:backup-user /opt/backups/scripts/system-backup.sh
Security: Never use chmod 777 on backup scripts. The script contains sensitive credentials and should only be readable by the backup user.

Create environment file

Store environment variables in a separate file for the systemd service. This keeps credentials separate from the script.

BORG_REPO=/opt/backups/repos/system-backup
BORG_PASSPHRASE=YourSecurePassphrase123!
PATH=/usr/local/bin:/usr/bin:/bin

Secure environment file

Set restrictive permissions on the environment file to protect credentials.

sudo chmod 600 /opt/backups/scripts/backup.env
sudo chown backup-user:backup-user /opt/backups/scripts/backup.env

Create systemd service unit

Define the systemd service that will execute the backup script. This runs as the backup user and loads the environment file.

[Unit]
Description=System Backup with BorgBackup
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
User=backup-user
Group=backup-user
EnvironmentFile=/opt/backups/scripts/backup.env
ExecStart=/opt/backups/scripts/system-backup.sh
StandardOutput=journal
StandardError=journal
SyslogIdentifier=system-backup

# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/opt/backups /var/log/backups
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes

Create systemd timer unit

Create the timer that will trigger the backup service. This example runs backups daily at 2 AM with randomization to avoid system load spikes.

[Unit]
Description=Run system backup daily
Requires=system-backup.service

[Timer]
# Run daily at 2 AM with 30 minute random delay
OnCalendar=daily
RandomizedDelaySec=30min
Persistent=true

# Prevent multiple instances
AccuracySec=1h

[Install]
WantedBy=timers.target

Enable and start the timer

Reload systemd configuration and enable the timer to start automatically. The timer will persist across reboots.

sudo systemctl daemon-reload
sudo systemctl enable system-backup.timer
sudo systemctl start system-backup.timer

Create backup monitoring script

Create a script to monitor backup status and send alerts if backups fail or become outdated.

#!/bin/bash

# Backup monitoring script
set -e

# Configuration
export BORG_REPO="/opt/backups/repos/system-backup"
export BORG_PASSPHRASE="YourSecurePassphrase123!"
LOG_FILE="/var/log/backups/backup-monitor.log"
MAX_AGE_HOURS=26  # Alert if last backup is older than 26 hours
ALERT_EMAIL="admin@example.com"

# Logging
exec 1> >(tee -a "$LOG_FILE")
exec 2>&1

echo "========== Backup Monitor Check: $(date) =========="

# Get last backup info
LAST_ARCHIVE=$(borg list --short --last 1 | head -n1)

if [ -z "$LAST_ARCHIVE" ]; then
    echo "ERROR: No backups found in repository"
    echo "No backups found" | mail -s "Backup Alert: No backups found" $ALERT_EMAIL 2>/dev/null || echo "Failed to send email alert"
    exit 1
fi

echo "Last backup archive: $LAST_ARCHIVE"

# Check backup age
ARCHIVE_INFO=$(borg info "::$LAST_ARCHIVE" --json)
LAST_BACKUP_TIME=$(echo $ARCHIVE_INFO | python3 -c "import sys, json; print(json.load(sys.stdin)['archives'][0]['start'])")
LAST_BACKUP_TIMESTAMP=$(date -d "$LAST_BACKUP_TIME" +%s)
CURRENT_TIMESTAMP=$(date +%s)
AGE_HOURS=$(( (CURRENT_TIMESTAMP - LAST_BACKUP_TIMESTAMP) / 3600 ))

echo "Last backup was $AGE_HOURS hours ago"

if [ $AGE_HOURS -gt $MAX_AGE_HOURS ]; then
    echo "WARNING: Last backup is older than $MAX_AGE_HOURS hours"
    echo "Last backup was $AGE_HOURS hours ago" | mail -s "Backup Alert: Backup overdue" $ALERT_EMAIL 2>/dev/null || echo "Failed to send email alert"
    exit 1
fi

# Check repository integrity (weekly)
DAY_OF_WEEK=$(date +%u)
if [ $DAY_OF_WEEK -eq 7 ]; then  # Sunday
    echo "Running weekly repository check"
    borg check --repository-only
    if [ $? -eq 0 ]; then
        echo "Repository integrity check passed"
    else
        echo "ERROR: Repository integrity check failed"
        echo "Repository check failed" | mail -s "Backup Alert: Repository corrupted" $ALERT_EMAIL 2>/dev/null || echo "Failed to send email alert"
        exit 1
    fi
fi

echo "Backup monitoring completed successfully"
echo "========== Monitor Check Complete: $(date) ========="
echo ""
exit 0

Set monitoring script permissions

Make the monitoring script executable and secure.

sudo chmod 750 /opt/backups/scripts/backup-monitor.sh
sudo chown backup-user:backup-user /opt/backups/scripts/backup-monitor.sh

Create monitoring systemd units

Create systemd service and timer for backup monitoring that runs every 6 hours.

[Unit]
Description=Backup Monitoring Check
After=network-online.target

[Service]
Type=oneshot
User=backup-user
Group=backup-user
EnvironmentFile=/opt/backups/scripts/backup.env
ExecStart=/opt/backups/scripts/backup-monitor.sh
StandardOutput=journal
StandardError=journal

Create monitoring timer

Create the timer for backup monitoring checks.

[Unit]
Description=Run backup monitoring every 6 hours
Requires=backup-monitor.service

[Timer]
OnCalendar=*-*-* 00,06,12,18:00:00
Persistent=true

[Install]
WantedBy=timers.target

Enable monitoring timer

Enable and start the monitoring timer.

sudo systemctl daemon-reload
sudo systemctl enable backup-monitor.timer
sudo systemctl start backup-monitor.timer

Create backup restore script

Create a utility script for easy backup restoration when needed.

#!/bin/bash

# Backup restoration script
set -e

# Configuration
export BORG_REPO="/opt/backups/repos/system-backup"
export BORG_PASSPHRASE="YourSecurePassphrase123!"

if [ $# -ne 2 ]; then
    echo "Usage: $0 

Set restore script permissions

Make the restore script executable.

sudo chmod 750 /opt/backups/scripts/restore-backup.sh
sudo chown backup-user:backup-user /opt/backups/scripts/restore-backup.sh

Verify your setup

Check that all components are working correctly and view backup status.

# Check timer status
sudo systemctl status system-backup.timer
sudo systemctl status backup-monitor.timer

# List active timers
sudo systemctl list-timers | grep backup

# Run a test backup manually
sudo systemctl start system-backup.service

# Check service logs
sudo journalctl -u system-backup.service -f

# View backup repository info
sudo -u backup-user borg info /opt/backups/repos/system-backup

# List available archives
sudo -u backup-user borg list /opt/backups/repos/system-backup
Note: The first backup will take the longest time as it creates the initial archive. Subsequent backups will be much faster due to deduplication.

Advanced configuration

Configure remote backup storage

For production environments, consider backing up to remote storage. Here's how to configure SSH-based remote backups.

# Generate SSH key for backup user
sudo -u backup-user ssh-keygen -t rsa -b 4096 -f /opt/backups/.ssh/id_rsa -N ""

# Copy public key to remote server
# sudo -u backup-user ssh-copy-id backup@remote-server.example.com

# Update backup script to use remote repository
</code><h2><code>export BORG_REPO="backup@remote-server.example.com:/backup/repos/system-backup"</code></h2></pre>
</div>

<div class="step">
### Configure email notifications
<p>Install mail utilities for backup alerts and notifications.</p>
<div class="distro-tabs">
<div class="tab-bar">


</div>
<div>
<pre class="terminal"><code>sudo apt install -y mailutils postfix
sudo dnf install -y mailx postfix

Common issues

SymptomCauseFix
"Permission denied" errorsIncorrect file ownership or permissionssudo chown -R backup-user:backup-user /opt/backups
"Repository not found" errorBORG_REPO path incorrect or not initializedCheck path and run borg init if needed
Timer not runningTimer not enabled or systemd not reloadedsudo systemctl daemon-reload && sudo systemctl enable --now system-backup.timer
Backup fails with passphrase errorWrong passphrase in environment fileCheck /opt/backups/scripts/backup.env
Large log files filling diskNo log rotation configuredConfigure logrotate for /var/log/backups/
Backup takes too longToo many files or wrong compressionAdjust exclude patterns or use faster compression

Backup retention and monitoring

Understanding and managing your backup retention policy is crucial for effective backup management. The configured retention policy keeps:

  • Daily backups for 7 days
  • Weekly backups for 4 weeks
  • Monthly backups for 6 months
  • Yearly backups for 1 year

Monitor disk usage regularly and adjust retention as needed. Use the monitoring script to ensure backups are running successfully and check for corruption weekly.

For comprehensive backup monitoring in production environments, consider integrating with system performance monitoring tools to track backup performance and resource usage.

Next steps

Automated install script

Run this to automate the entire setup

इसे खुद मैनेज नहीं करना चाहते?

हम उन businesses के लिए infrastructure संभालते हैं जो uptime पर निर्भर हैं। Fully managed, एक fixed contact के साथ जो आपके setup को जानता है।

आपको एक निश्चित contact मिलता है जो आपके setup को जानता है

Rotterdam 04:09 · एक message में पहुंचें, कोई ticket form नहीं