How to Set Up Automated Backups with BorgBackup on Linux: Deduplication, Encryption, and Peace of Mind

Why Backups Are the Most Important Thing You’re Not Doing

Here’s a truth that every sysadmin learns eventually, usually the hard way: your data is only as safe as your last backup. Disks fail. Ransomware happens. One wrong rm -rf can erase months of work. And yet, backups remain the most neglected part of running any server.

I’ve been there. I once lost a full week of configuration work because I thought my RAID array was enough. (It wasn’t. RAID is not a backup.) Since then, I’ve become religious about backups — and the tool that changed everything for me is BorgBackup.

BorgBackup (or just “Borg”) is a deduplicating backup program. It compresses your data, encrypts it, and only stores the parts that have changed since the last backup. The result? A backup system that’s fast, efficient, and secure. And in this tutorial, I’ll show you how to set it up with fully automated, scheduled backups.

What You’ll Need

  • A Linux server (Ubuntu 22.04+ or Debian 12+ recommended)
  • sudo access
  • A destination for your backups — this can be a separate disk, a NAS, or even a remote server over SSH
  • About 15 minutes

Step 1: Install BorgBackup

On Ubuntu or Debian, Borg is in the default repositories:

sudo apt update
sudo apt install borgbackup -y

On Fedora:

sudo dnf install borgbackup -y

On Arch:

sudo pacman -S borg

Verify the installation:

borg --version

You should see something like borg 1.2.x or newer. This tutorial works with Borg 1.2+.

Step 2: Choose Your Backup Destination

Borg can back up to a local directory or a remote server over SSH. For a homelab, I strongly recommend backing up to a separate machine — if your main server dies, you don’t want your backups dying with it.

For this tutorial, I’ll cover both scenarios. Let’s say:

  • Source server: 192.168.1.10 (the machine you’re backing up)
  • Backup server: 192.168.1.20 (where backups will be stored)
  • User on backup server: backup

First, create a dedicated backup directory on the destination:

# On the backup server (192.168.1.20)
sudo mkdir -p /backups/borg-repo
sudo chown backup:backup /backups/borg-repo

Step 3: Set Up SSH Key Authentication

For automated backups, you need passwordless SSH access from the source to the backup server. Generate an SSH key on the source machine:

ssh-keygen -t ed25519 -f ~/.ssh/borg_backup_key -C "borg-backup"

Press Enter when prompted for a passphrase — we want this to be fully automated. (The Borg repo itself is encrypted, so the SSH key only needs to transport data.)

Copy the public key to the backup server:

ssh-copy-id -i ~/.ssh/borg_backup_key.pub [email protected]

Test that it works without a password:

ssh -i ~/.ssh/borg_backup_key [email protected] "echo connected"

If you see connected, you’re good to go.

Step 4: Initialize the Borg Repository

Now create the Borg repository on the backup server. This is where all your deduplicated, compressed, encrypted data will live:

export BORG_REPO="ssh://[email protected]/backups/borg-repo"
export BORG_PASSPHRASE="your-strong-passphrase-here"

borg init --encryption=repokey-blake2 $BORG_REPO

Let’s break down what’s happening here:

  • repokey-blake2 — This encryption mode stores the encryption key inside the repository (encrypted with your passphrase). It uses the BLAKE2 hash function, which is fast and secure. There’s also keyfile mode if you want to store the key separately.
  • BORG_PASSPHRASE — This encrypts the repository. Do not forget this. If you lose the passphrase, your backups are gone forever. Store it in a password manager.

You should see output confirming the repository was initialized.

Step 5: Run Your First Backup

Let’s do a test run. I’ll back up /etc (system configuration) and /home (user data):

borg create \
    --verbose \
    --filter AME \
    --list \
    --stats \
    --show-rc \
    --compression lz4 \
    --exclude-caches \
    --exclude '/home/*/.cache/*' \
    --exclude '/var/cache/*' \
    --exclude '/var/tmp/*' \
    $BORG_REPO::{hostname}-{now:%Y-%m-%d-%H%M%S} \
    /etc \
    /home \
    /var/www

That’s a lot of flags! Here’s what they do:

  • --verbose — Show what’s being backed up
  • --list — List each file as it’s processed
  • --stats — Show summary statistics at the end
  • --compression lz4 — Fast compression. Use zstd for better compression ratios if CPU isn’t a concern.
  • --exclude-caches — Skip directories marked as caches (CACHEDIR.TAG)
  • {hostname}-{now:%Y-%m-%d-%H%M%S} — Names the archive with your hostname and timestamp

After it finishes, you’ll see stats like the number of files, original size, compressed size, and deduplicated size. The deduplication ratio is often surprisingly good — especially for subsequent backups.

Verify the backup exists:

borg list $BORG_REPO

Step 6: Automate with a Systemd Timer

Manual backups don’t count. Let’s set up a daily automated backup using systemd.

First, create a script at /usr/local/bin/borg-backup.sh:

sudo tee /usr/local/bin/borg-backup.sh > /dev/null << 'EOF'
#!/bin/bash
set -euo pipefail

# Configuration
export BORG_REPO="ssh://[email protected]/backups/borg-repo"
export BORG_PASSPHRASE="your-strong-passphrase-here"

# SSH key for remote backup
export BORG_RSH="ssh -i /root/.ssh/borg_backup_key -o StrictHostKeyChecking=no"

# Log output
exec >(logger -t borg-backup -p local0.info) 2>&1

echo "Starting Borg backup: $(date)"

# Create backup
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 '/proc/*' \
    --exclude '/sys/*' \
    --exclude '/dev/*' \
    --exclude '/run/*' \
    --exclude '/mnt/*' \
    --exclude '/media/*' \
    $BORG_REPO::{hostname}-{now:%Y-%m-%d-%H%M%S} \
    /etc \
    /home \
    /var/www \
    /opt

echo "Backup completed: $(date)"

# Prune old backups - keep a sensible retention policy
borg prune \
    --list \
    --stats \
    --show-rc \
    --keep-daily 7 \
    --keep-weekly 4 \
    --keep-monthly 6 \
    --keep-yearly 2 \
    $BORG_REPO

echo "Pruning completed: $(date)"

# Compact the repository to free space
borg compact $BORG_REPO

echo "All done: $(date)"
EOF

sudo chmod +x /usr/local/bin/borg-backup.sh

Now create the systemd service file:

sudo tee /etc/systemd/system/borg-backup.service > /dev/null << 'EOF'
[Unit]
Description=BorgBackup automated backup
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/borg-backup.sh
Nice=19
IOSchedulingClass=best-effort
IOSchedulingPriority=7
EOF

And the timer that runs it daily at 2 AM:

sudo tee /etc/systemd/system/borg-backup.timer > /dev/null << 'EOF'
[Unit]
Description=Run BorgBackup daily

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

[Install]
WantedBy=timers.target
EOF

Enable and start the timer:

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

# Verify it's active
systemctl list-timers borg-backup.timer

The RandomizedDelaySec=1800 adds a random delay of up to 30 minutes — this is useful if you’re backing up multiple servers to the same destination and want to avoid them all hitting the disk at the same time. Persistent=true ensures that if the server was off at 2 AM, the backup runs the next time it boots.

Step 7: Test Your Restores

This is the step everyone skips. Don’t skip it. A backup you’ve never tested is not a backup — it’s a hope and a prayer.

List your archives:

borg list $BORG_REPO

Extract a single file to verify:

borg extract $BORG_REPO::your-server-2026-06-18-020000 etc/hostname

Or mount the entire archive as a filesystem to browse it:

mkdir -p /tmp/borg-mount
borg mount $BORG_REPO::your-server-2026-06-18-020000 /tmp/borg-mount

# Browse the backup
cd /tmp/borg-mount
ls -la etc/

# When done
borg umount /tmp/borg-mount

The mount feature is incredibly useful. It lets you browse your backup like a regular directory, grab individual files, and verify that everything you need is actually there.

Common Pitfalls and Troubleshooting

“Passphrase wrong” errors

Double-check that BORG_PASSPHRASE is set correctly in your backup script. A common mistake is having a trailing space or newline. Use echo -n "your-passphrase" when setting it.

SSH connection failures

Make sure the SSH key permissions are correct (chmod 600 ~/.ssh/borg_backup_key). Also verify that the backup user on the remote server has write access to the repository directory.

Repository locked

If a previous backup was interrupted (power failure, crash), Borg may leave a lock file. You can break the lock with:

borg break-lock $BORG_REPO

But only do this if you’re sure no other backup process is running.

Running out of disk space on the backup server

Borg’s deduplication helps a lot, but you still need to prune old backups. The retention policy in our script (7 daily, 4 weekly, 6 monthly, 2 yearly) is a good starting point. Adjust based on your storage capacity and needs.

Forgetting the passphrase

There is no recovery. I’m serious. Write it down, store it in a password manager, and maybe keep a sealed copy in a safe. If you lose the passphrase, your backups are cryptographically inaccessible.

Going Further

Once you have the basics working, here are some next steps:

  • Multiple servers, one repo: You can store backups from multiple machines in the same Borg repository. Just use different archive name prefixes.
  • BorgBase: If you don’t have a second machine, BorgBase offers cheap, Borg-specific remote storage with append-only mode for ransomware protection.
  • Healthchecks.io monitoring: Add a ping to Healthchecks.io at the end of your backup script. If a backup fails and doesn’t ping, you’ll get an alert.
  • Borgmatic: If you prefer a configuration-file approach over shell scripts, borgmatic wraps Borg with a YAML config and handles pruning, scheduling, and notifications.

Wrapping Up

Setting up BorgBackup takes about 15 minutes, and it might save you one day from a very bad week. The combination of deduplication, encryption, and compression makes it one of the most efficient backup tools available — and it’s completely open source.

The 3-2-1 rule of backups says: 3 copies of your data, on 2 different media, with 1 offsite. Borg makes the first two trivially easy. For the third, push your Borg repo to a remote server or BorgBase.

Your future self — the one recovering from a disk failure at 2 AM — will thank you for setting this up today.

Leave a Reply

Your email address will not be published. Required fields are marked *