How to Install and Configure BorgBackup on Fedora Server: Automating Data Backups over SSH

Fedora tutorial - IT technology blog
Fedora tutorial - IT technology blog

1. Real-World Dilemma: Storage Disasters and the Pitfalls of Traditional Rsync

Early in my sysadmin career, I learned a painful lesson I will never forget. A production cluster running MySQL alongside an uploads directory holding customer images grew rapidly past 320GB. To play it safe, I wrote a shell script to package everything into a tar.gz archive and pushed it to an offsite backup server using rsync at 2:00 AM every night.

Two weeks later, alerting alarms went off non-stop. The 2TB drive on the backup server hit 100% capacity (Disk Full). To make matters worse, ransomware struck our primary server. All the unencrypted .tar.gz files sitting exposed on the backup server were compromised as well due to the lack of an append-only snapshot isolation mechanism. Recovery dragged on for more than 18 hours because the massive compressed archives saturated our entire 1Gbps internal network link.

2. Root Cause Analysis: Why Do Legacy Backup Solutions Fall Short?

Analyzing what went wrong, I identified three fatal flaws that brought the system down:

  • Wasted Disk Space Due to Missing Deduplication: Daily source code or database dumps usually change by only 3-5%. Yet tar.gz compresses and stores 100% of that data every single run. Keeping 10 backup archives effectively multiplies storage consumption tenfold.
  • Weak Security Posture: Data both in transit and at rest remained unencrypted plaintext. Anyone who breached the backup server could easily steal the entire customer database.
  • Heavy Rsync Overhead on Giant Files: When handling partially modified 50-80GB SQL dump files, rsync spent hours just calculating block checksums, pegging the source server’s CPU at 100%.

3. Evaluating Backup Strategies on Linux

Faced with this challenge, infrastructure engineers typically consider three main options:

  • Approach 1: Transferring Btrfs or ZFS Snapshots over SSH. Delivers lightning-fast block-level replication. The drawback is that both source and destination must share the same Btrfs/ZFS filesystem, which is often impractical on standard VPS providers or budget cloud storage.
  • Approach 2: Using Restic or Duplicity. Restic supports multiple storage backends such as S3 and MinIO. However, once repositories swell to millions of chunks on bare-metal servers, Restic consumes significant RAM (frequently spiking to 4-8GB during pruning operations).
  • Approach 3: Deploying BorgBackup (Borg). Built specifically for deduplication in C and Python, Borg splits files into variable-sized chunks, hashes them, and encrypts everything on the client side before transmission over SSH. Any chunk already present on the target repository is skipped immediately.

4. The Best Practice: Setting Up BorgBackup on Fedora Server with SSH Automation

On Fedora Server, BorgBackup stands out as the optimal choice: memory-efficient, stable, and consistently updated with upstream releases in the official package repositories.

Step 1: Install BorgBackup on Both Machines

Borg must be installed on both nodes: the Fedora Server (source) and the Backup Storage Server (target). On Fedora, install it via DNF:

sudo dnf install -y borgbackup

On the Backup Server (Ubuntu, Debian, or CentOS/Fedora), install the corresponding package:

# If the backup server runs Ubuntu/Debian
sudo apt update && sudo apt install -y borgbackup

Step 2: Configure a Dedicated SSH Key for Backups

To run automated jobs non-interactively without password prompts, generate a dedicated SSH key pair on the Fedora Server:

# Generate an ed25519 key without passphrase for the root user
sudo ssh-keygen -t ed25519 -f /root/.ssh/id_borg_ed25519 -N ""

# Copy the public key to the Backup Server
sudo ssh-copy-id -i /root/.ssh/id_borg_ed25519.pub [email protected]

Verify connectivity with a test command:

sudo ssh -i /root/.ssh/id_borg_ed25519 [email protected] "echo 'SSH connection successful!'"

Step 3: Initialize the Encrypted Repository (Borg Init)

The repokey-blake2 encryption mode is recommended for its high hashing performance and robust AES-256 data protection. Run the repository initialization command from the Fedora Server:

export BORG_RSH="ssh -i /root/.ssh/id_borg_ed25519"
export BORG_PASSPHRASE="YourSuperSecurePassword123@#"

# Initialize the repo at /data/backups/fedora-app on the target server
borg init --encryption=repokey-blake2 [email protected]:/data/backups/fedora-app

Crucial note: Always export the key to a separate file and store it safely in Bitwarden or on an offline USB drive. Losing your key means permanent data loss:

borg key export [email protected]:/data/backups/fedora-app /root/borg-key-backup.txt

Step 4: Create Backup and Pruning Scripts

Create a shell script at /usr/local/bin/borg-backup.sh:

sudo nano /usr/local/bin/borg-backup.sh

The script uses lz4 compression for maximum throughput and retains archives for the past 7 days, 4 weeks, and 6 months:

#!/usr/bin/env bash
set -euo pipefail

# Environment configuration
export BORG_REPO="[email protected]:/data/backups/fedora-app"
export BORG_PASSPHRASE="YourSuperSecurePassword123@#"
export BORG_RSH="ssh -i /root/.ssh/id_borg_ed25519 -o BatchMode=yes"

ARCHIVE_NAME="::fedora-backup-$(date +'%Y-%m-%d_%H%M%S')"

echo "[$(date)] Starting BorgBackup process..."

# Backup critical directories, exclude cache data
borg create \
    --verbose \
    --stats \
    --compression lz4 \
    --exclude-caches \
    "$BORG_REPO$ARCHIVE_NAME" \
    /etc \
    /var/www \
    /home

echo "[$(date)] Pruning snapshots based on retention policy..."

# Retain: 7 days, 4 weeks, 6 months
borg prune \
    --list \
    --keep-daily=7 \
    --keep-weekly=4 \
    --keep-monthly=6 \
    "$BORG_REPO"

echo "[$(date)] Backup completed securely!"

Restrict file permissions so only root can read the passphrase:

sudo chmod 700 /usr/local/bin/borg-backup.sh

Step 5: Schedule Automated Backups with Systemd Timers instead of Cron

Systemd timers provide structured logging through journalctl and prevent overlapping execution if a prior backup job is still running.

Create the service unit at /etc/systemd/system/borg-backup.service:

[Unit]
Description=BorgBackup Automated Job
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/borg-backup.sh
StandardOutput=journal
StandardError=journal

Create the corresponding timer unit at /etc/systemd/system/borg-backup.timer to trigger every day at 02:00 AM:

[Unit]
Description=Run BorgBackup Daily at 02:00 AM

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target

Reload systemd configurations and enable the timer:

sudo systemctl daemon-reload
sudo systemctl enable --now borg-backup.timer

# Verify upcoming timer execution
sudo systemctl list-timers borg-backup.timer

Step 6: Verify Snapshots and Practice Restores

A backup strategy is only as good as its restore drill. To list all snapshots currently stored in the repository:

export BORG_RSH="ssh -i /root/.ssh/id_borg_ed25519"
export BORG_PASSPHRASE="YourSuperSecurePassword123@#"

borg list [email protected]:/data/backups/fedora-app

Extract just the /etc directory from any snapshot to verify archive integrity:

mkdir -p /mnt/recovery && cd /mnt/recovery

# Extract /etc only (note: omit the leading slash)
borg extract [email protected]:/data/backups/fedora-app::fedora-backup-2026-10-20_020000 etc

In our production setup, maintaining 30 days of continuous backups for 320GB of source data consumed merely 48GB of actual storage on the backup server. With Borg, you save up to 85% in storage costs while gaining solid resilience against hardware failures and ransomware threats.

Share: