Configure Linux system backup automation with rsync and systemd timers

Intermediate 25 min Apr 03, 2026 31 views
Ubuntu 24.04 Ubuntu 22.04 Debian 12 AlmaLinux 9 Rocky Linux 9 Fedora 41

Set up automated Linux system backups using rsync with SSH authentication, systemd timers for scheduling, retention policies, email notifications, and monitoring. Perfect for production environments requiring reliable backup automation.

Prerequisites

  • Root or sudo access
  • At least 10GB free disk space for local backups
  • Network connectivity for remote backups
  • Remote server with SSH access (optional)

What this solves

System backups are critical for data protection, but manual backups are unreliable and time-consuming. This tutorial shows you how to configure automated Linux system backups using rsync for efficient file synchronization, systemd timers for precise scheduling, and proper retention policies to manage storage. You'll also set up email notifications for backup success and failure monitoring.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you have the latest versions of all required tools.

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

Install required packages

Install rsync, SSH client, and mail utilities for backup operations and notifications.

sudo apt install -y rsync openssh-client mailutils postfix
sudo dnf install -y rsync openssh-clients mailx postfix

Create backup user and directories

Create a dedicated backup user with proper permissions for security isolation.

sudo useradd --system --shell /bin/bash --home /opt/backup --create-home backup
sudo mkdir -p /opt/backup/{scripts,logs,local-backup}
sudo chown -R backup:backup /opt/backup
sudo chmod 755 /opt/backup
sudo chmod 750 /opt/backup/{scripts,logs,local-backup}

Generate SSH key for remote backups

Create an SSH key pair for passwordless authentication to remote backup servers.

sudo -u backup ssh-keygen -t ed25519 -f /opt/backup/.ssh/backup_key -N "" -C "backup@$(hostname)"
sudo -u backup chmod 700 /opt/backup/.ssh
sudo -u backup chmod 600 /opt/backup/.ssh/backup_key
sudo -u backup chmod 644 /opt/backup/.ssh/backup_key.pub

Configure SSH client settings

Create SSH configuration for consistent connection settings and security.

Host backup-server
    HostName 203.0.113.10
    User backup
    Port 22
    IdentityFile /opt/backup/.ssh/backup_key
    StrictHostKeyChecking no
    UserKnownHostsFile /opt/backup/.ssh/known_hosts
    ServerAliveInterval 60
    ServerAliveCountMax 3
    Compression yes
sudo chown backup:backup /opt/backup/.ssh/config
sudo chmod 600 /opt/backup/.ssh/config
Important: Copy the public key content from /opt/backup/.ssh/backup_key.pub to your remote backup server's authorized_keys file for the backup user.

Create backup configuration file

Define backup sources, destinations, and retention settings in a central configuration file.

# Backup Configuration
BACKUP_NAME="$(hostname)-system"
LOCAL_BACKUP_DIR="/opt/backup/local-backup"
REMOTE_BACKUP_HOST="backup-server"
REMOTE_BACKUP_DIR="/backup/servers/$(hostname)"
LOG_FILE="/opt/backup/logs/backup.log"
RETENTION_DAYS=30
EMAIL_RECIPIENT="admin@example.com"

Source directories to backup (space-separated)

BACKUP_SOURCES="/etc /var/log /var/www /home /opt/important-data"

Exclude patterns (space-separated)

EXCLUDE_PATTERNS="/var/log/.log. /var/log/journal/ /tmp/ /var/tmp/ /proc/ /sys/ /dev/ /run/*"

Rsync options

RSYNC_OPTS="-avz --delete --delete-excluded --numeric-ids --hard-links --acls --xattrs"
sudo chown backup:backup /opt/backup/scripts/backup.conf
sudo chmod 640 /opt/backup/scripts/backup.conf

Create main backup script

Build the main backup script with logging, error handling, and retention management.

#!/bin/bash

Load configuration

source /opt/backup/scripts/backup.conf

Function to log messages

log_message() { echo "$(date '+%Y-%m-%d %H:%M:%S'): $1" | tee -a "$LOG_FILE" }

Function to send email notification

send_notification() { local subject="$1" local message="$2" echo "$message" | mail -s "[$(hostname)] $subject" "$EMAIL_RECIPIENT" }

Function to cleanup old backups

cleanup_old_backups() { log_message "Cleaning up backups older than $RETENTION_DAYS days" find "$LOCAL_BACKUP_DIR" -type d -name "$BACKUP_NAME-*" -mtime +$RETENTION_DAYS -exec rm -rf {} + 2>/dev/null # Remote cleanup if SSH connection works if ssh "$REMOTE_BACKUP_HOST" "test -d $REMOTE_BACKUP_DIR" 2>/dev/null; then ssh "$REMOTE_BACKUP_HOST" "find $REMOTE_BACKUP_DIR -type d -name '$BACKUP_NAME-*' -mtime +$RETENTION_DAYS -exec rm -rf {} +" 2>/dev/null fi }

Function to perform backup

perform_backup() { local timestamp=$(date +%Y%m%d_%H%M%S) local backup_dest="$LOCAL_BACKUP_DIR/$BACKUP_NAME-$timestamp" local rsync_success=true log_message "Starting backup to $backup_dest" # Create backup directory mkdir -p "$backup_dest" # Build exclude options for rsync local exclude_opts="" for pattern in $EXCLUDE_PATTERNS; do exclude_opts="$exclude_opts --exclude='$pattern'" done # Perform local backup for source in $BACKUP_SOURCES; do if [ -d "$source" ] || [ -f "$source" ]; then log_message "Backing up $source" eval "rsync $RSYNC_OPTS $exclude_opts '$source' '$backup_dest/'" if [ $? -ne 0 ]; then log_message "ERROR: Failed to backup $source" rsync_success=false fi else log_message "WARNING: Source $source does not exist, skipping" fi done # Create backup metadata cat > "$backup_dest/backup_info.txt" << EOF Backup Date: $(date) Hostname: $(hostname) Backup Sources: $BACKUP_SOURCES Backup Size: $(du -sh "$backup_dest" | cut -f1) Rsync Options: $RSYNC_OPTS EOF if [ "$rsync_success" = true ]; then log_message "Local backup completed successfully" # Sync to remote server if ssh "$REMOTE_BACKUP_HOST" "mkdir -p $REMOTE_BACKUP_DIR" 2>/dev/null; then log_message "Syncing to remote server" rsync $RSYNC_OPTS "$backup_dest/" "$REMOTE_BACKUP_HOST:$REMOTE_BACKUP_DIR/$BACKUP_NAME-$timestamp/" if [ $? -eq 0 ]; then log_message "Remote backup completed successfully" send_notification "Backup Success" "Backup completed successfully at $timestamp\nLocal: $backup_dest\nRemote: $REMOTE_BACKUP_HOST:$REMOTE_BACKUP_DIR/$BACKUP_NAME-$timestamp" else log_message "ERROR: Remote backup failed" send_notification "Backup Warning" "Local backup succeeded but remote sync failed at $timestamp\nLocal: $backup_dest" fi else log_message "WARNING: Cannot connect to remote server, keeping local backup only" send_notification "Backup Warning" "Local backup succeeded but remote server unavailable at $timestamp\nLocal: $backup_dest" fi else log_message "ERROR: Local backup failed" send_notification "Backup Failed" "Backup failed at $timestamp\nCheck log: $LOG_FILE" return 1 fi }

Main execution

log_message "=== Backup process started ==="nperform_backup cleanup_old_backups log_message "=== Backup process completed ==="
sudo chown backup:backup /opt/backup/scripts/backup.sh
sudo chmod 750 /opt/backup/scripts/backup.sh

Create systemd service unit

Define the systemd service that will execute the backup script with proper user context and security settings.

[Unit]
Description=System Backup Service
Wants=network-online.target
After=network-online.target
Requires=local-fs.target

[Service]
Type=oneshot
User=backup
Group=backup
ExecStart=/opt/backup/scripts/backup.sh
WorkingDirectory=/opt/backup
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
StandardOutput=journal
StandardError=journal
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/opt/backup
NoNewPrivileges=true
RestrictSUIDSGID=true

Create systemd timer unit

Configure the systemd timer to run backups automatically on a daily schedule.

[Unit]
Description=Daily System Backup Timer
Requires=system-backup.service

[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=1800
AccuracySec=1m

[Install]
WantedBy=timers.target

Enable and start the backup timer

Reload systemd configuration and enable the timer to start backups automatically.

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

Configure log rotation

Set up logrotate to manage backup log files and prevent disk space issues.

/opt/backup/logs/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    create 640 backup backup
    postrotate
        systemctl reload-or-restart rsyslog > /dev/null 2>&1 || true
    endscript
}

Verify your setup

Test the backup system to ensure everything works correctly before relying on automated scheduling.

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

List all active timers

sudo systemctl list-timers system-backup.timer

Test backup manually

sudo systemctl start system-backup.service

Check service status

sudo systemctl status system-backup.service

View backup logs

sudo tail -f /opt/backup/logs/backup.log

Check backup directory

sudo ls -la /opt/backup/local-backup/

Test SSH connection to remote server

sudo -u backup ssh backup-server "echo 'Connection successful'"

View systemd journal for backup service

journalctl -u system-backup.service -f

Common issues

SymptomCauseFix
Permission denied on backup directoriesIncorrect ownership or permissionsFix with sudo chown -R backup:backup /opt/backup and verify directory permissions are 750
SSH connection fails to remote serverKey not authorized or network issuesCopy public key to remote server, test with sudo -u backup ssh backup-server
Timer not running backupsTimer not enabled or service failedCheck with sudo systemctl list-timers and enable with sudo systemctl enable system-backup.timer
Email notifications not sentPostfix not configured properlyConfigure Postfix with sudo dpkg-reconfigure postfix and test with echo "test" | mail admin@example.com
Backup script fails with rsync errorsSource directories don't exist or access deniedCheck source paths in backup.conf and verify backup user has read access
Disk space full from old backupsRetention cleanup not workingVerify RETENTION_DAYS setting and manually clean with find /opt/backup/local-backup -mtime +30 -delete
Service fails to startSystemd security restrictions too strictCheck journal with journalctl -u system-backup.service and adjust ReadWritePaths if needed

Next steps

Automated install script

Run this to automate the entire setup

#rsync #systemd #backup #automation #ssh

Need help?

Don't want to manage this yourself?

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

Talk to an engineer