BorgBackup Raspberry Pi Prune Policies: Complete Setup Guide

BorgBackup Raspberry Pi Prune Setup

BorgBackup Raspberry Pi prune policies give you automated, deduplicated, encrypted backups that reclaim storage on a defined schedule. Borg stores each backup as an archive in a repository, deduplicates data across archives at the chunk level, and applies retention rules with borg prune to remove archives that no longer need to be kept. This guide covers installation, repository initialisation, a working backup script, prune policy configuration, restoration, remote backups via SSH, and ongoing maintenance.

Last tested: Raspberry Pi OS Bookworm Lite 64-bit | December 19, 2025 | Raspberry Pi 4 Model B (4GB) | BorgBackup 1.2.7 | USB SSD backup target

Key Takeaways

  • Always run borg prune after a successful backup, never before. If the new backup fails and you have already pruned the old archives, you are left with no valid backup.
  • Store the Borg passphrase in a dedicated credentials file with restricted permissions rather than in the backup script itself. Use BORG_PASSCOMMAND to read it at runtime.
  • Use borg mount to browse an archive before extracting. This confirms the archive contains what you expect without unpacking the full dataset.
  • Run borg check weekly on active repositories. Add --verify-data monthly for a deeper integrity check.
  • Use UUIDs for mount points in backup scripts, not device paths like /dev/sda1. Device paths change when drives are reconnected in a different order.
  • Test restores regularly. A backup that has never been restored is an untested assumption.
BorgBackup Raspberry Pi prune policies diagram showing backup workflow from source to Borg repository with prune retention schedule

Why BorgBackup Raspberry Pi Prune Policies Work Well Together

Borg’s three core features (deduplication, encryption, and compression) make it well suited to running on modest hardware like the Pi 4. Deduplication splits data into variable-size chunks and stores each unique chunk once. When you back up the same files with small changes between runs, Borg only stores the changed chunks. This keeps the repository small even with daily archives spanning months.

Prune policies control how many archives are retained over time. Without pruning, the repository grows indefinitely. With a well-defined policy, older archives are removed on a schedule while preserving the granularity you need for recovery: recent dailies, weekly snapshots, and monthly milestones.

Borg vs rsync for Raspberry Pi backups

FeatureBorgBackuprsync
DeduplicationYes (chunk-level)No
EncryptionBuilt-inNo (SSH transport only)
CompressionYes (lz4, zstd, zlib)No
Versioned archivesYesWith --backup-dir workaround
Repository integrity checkborg checkNo
Browse without extractingborg mountNo
Remote backupSSH nativeSSH native

Hardware Setup

Pi 4 with 2GB RAM is sufficient for typical Borg workloads. The bottleneck for compression and deduplication is CPU rather than RAM, and Pi 4 handles both without sustained throttling at typical backup sizes. Pi 3 works for smaller datasets. Pi 5 provides headroom for large repositories or --verify-data checks.

  • Broadcom BCM2711, quad-core Cortex-A72 (ARM v8) 64-bit SoC @ 1. 5GHz
  • 2. 4 GHz and 5. 0 GHz IEEE 802. 11b/g/n/ac wireless LAN, Bluetooth 5. 0, BLE
  • 2 × USB 3. 0 ports, 2 x USB 2. 0 Ports

Use a USB SSD for the Borg repository target rather than microSD. SD cards wear out under sustained write loads and are not reliable for backup storage. A powered USB enclosure avoids drawing too much current from the Pi’s USB ports. See Booting Raspberry Pi from USB SSD for USB storage setup. For reducing write pressure on the OS SD card, see Setting Up zram on Raspberry Pi.

Monitor for voltage throttling on the Pi, especially with a USB drive attached:

vcgencmd get_throttled
# 0x0 means no throttling -- any other value indicates a problem

Step 1: Install BorgBackup

sudo apt update
sudo apt install borgbackup -y
borg --version

For a more recent version than the Bookworm repository provides, install from PyPI:

pip install borgbackup --break-system-packages

Expected result: borg --version returns 1.2.x or higher.

Step 2: Initialise the Repository

Create the directory that will hold the repository, then initialise it with encryption:

sudo mkdir -p /mnt/backup/borg
sudo chown pi:pi /mnt/backup/borg

# Initialise with repokey encryption (passphrase-protected, key stored in repo)
borg init --encryption=repokey /mnt/backup/borg

Choose the encryption mode carefully:

ModeKey storedUse case
repokeyInside repositoryLocal or trusted remote backup targets
keyfileOn client machineUntrusted remote targets (key never leaves client)
repokey-blake2Inside repositoryAs above but faster MAC on modern hardware
noneN/ANo encryption. Not recommended.

After initialisation, export a backup of the repository key:

borg key export /mnt/backup/borg ~/borg-key-backup.txt
# Store this file off-device and off-Pi -- losing it means losing access to all archives

Expected result: borg info /mnt/backup/borg returns repository statistics without error. The key backup file exists and is readable.

Step 3: Create the Backup Script

Store the passphrase in a dedicated file with restricted permissions rather than embedding it in the script:

echo 'your-strong-passphrase' | sudo tee /etc/borg-passphrase > /dev/null
sudo chmod 600 /etc/borg-passphrase
sudo chown root:root /etc/borg-passphrase

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

#!/bin/bash
set -euo pipefail

export BORG_REPO=/mnt/backup/borg
export BORG_PASSCOMMAND='cat /etc/borg-passphrase'

LOG=/var/log/borg-backup.log
exec >> "$LOG" 2>&1
echo "--- Backup started: $(date) ---"

# Create archive
borg create \
  --stats \
  --compression lz4 \
  "$BORG_REPO::$(hostname)-$(date +%Y-%m-%dT%H:%M)" \
  /etc \
  /home \
  /var/log \
  --exclude /home/*/.cache

BACKUP_EXIT=$?

if [ "$BACKUP_EXIT" -eq 0 ]; then
  echo "Backup succeeded. Running prune..."
  borg prune \
    --list \
    --keep-daily=7 \
    --keep-weekly=4 \
    --keep-monthly=6 \
    --keep-yearly=1 \
    "$BORG_REPO"
  echo "Prune complete."
else
  echo "Backup failed with exit code $BACKUP_EXIT -- skipping prune."
fi

echo "--- Backup finished: $(date) ---"
sudo chmod 750 /usr/local/bin/borg-backup.sh

Expected result: Running the script manually completes without errors. borg list /mnt/backup/borg shows the new archive. /var/log/borg-backup.log contains statistics including original size, deduplicated size, and compression ratio.

Step 4: Configure BorgBackup Raspberry Pi Prune Policies

The borg prune command removes archives that fall outside the retention rules. It reads right-to-left through the archive list and keeps the specified number of archives per time period. Archives that satisfy no retention rule are deleted.

FlagKeepsExample
--keep-dailyN most recent daily archives--keep-daily=7 keeps one archive per day for 7 days
--keep-weeklyN most recent weekly archives--keep-weekly=4 keeps one per week for 4 weeks
--keep-monthlyN most recent monthly archives--keep-monthly=6 keeps one per month for 6 months
--keep-yearlyN most recent yearly archives--keep-yearly=1 keeps one per year
--keep-withinAll archives within a time window--keep-within=30d keeps everything from the last 30 days

The policy in the script above (--keep-daily=7 --keep-weekly=4 --keep-monthly=6 --keep-yearly=1) retains a week of daily granularity, a month of weekly snapshots, six months of monthly checkpoints, and one annual archive. This covers most recovery scenarios without excessive storage growth.

Preview what a prune would remove without making changes:

export BORG_PASSCOMMAND='cat /etc/borg-passphrase'
borg prune --dry-run --list \
  --keep-daily=7 --keep-weekly=4 --keep-monthly=6 \
  /mnt/backup/borg

Step 5: Automate with systemd Timer

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

[Unit]
Description=BorgBackup daily backup
After=network.target local-fs.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/borg-backup.sh
User=root

Create a timer unit at /etc/systemd/system/borg-backup.timer:

[Unit]
Description=Run BorgBackup daily

[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=1800

[Install]
WantedBy=timers.target

Persistent=true ensures the backup runs on the next boot if the Pi was off when the scheduled time passed. RandomizedDelaySec=1800 spreads the start time by up to 30 minutes to avoid contention with other scheduled tasks.

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

Expected result: systemctl list-timers shows the borg-backup.timer with a next trigger time. After the first scheduled run, the log at /var/log/borg-backup.log contains a completed backup entry.

Restoring from a Backup

Browse an archive before restoring

export BORG_PASSCOMMAND='cat /etc/borg-passphrase'

# List all archives
borg list /mnt/backup/borg

# Mount a specific archive
sudo mkdir -p /mnt/restore
borg mount /mnt/backup/borg::pi4-2025-12-19T02:00 /mnt/restore

# Browse the mounted archive
ls /mnt/restore/etc/

# Unmount when done
borg umount /mnt/restore

Extract a single file

# Extract a specific file (path is relative to the backup root)
borg extract /mnt/backup/borg::pi4-2025-12-19T02:00 etc/nginx/nginx.conf

# The file is restored to the current working directory

Full system restore

# Boot from a separate SD card or live OS
# Mount the target drive
sudo mount /dev/sda2 /mnt/target

# Extract archive to target
cd /mnt/target
borg extract /mnt/backup/borg::pi4-2025-12-19T02:00

# Fix fstab and cmdline.txt UUIDs to match the target drive
# before booting from the restored system

After a full restore, verify the UUIDs in /mnt/target/etc/fstab match the actual partition UUIDs of the target drive using blkid. A mismatch prevents the restored OS from booting.

Remote Backups via SSH

Borg supports initialising and writing to a repository on a remote host over SSH. The remote host must have Borg installed:

# Initialise a remote repository
borg init --encryption=keyfile user@backup-host:/backup/pi-repo

# Back up to the remote repository
export BORG_REPO=ssh://user@backup-host/backup/pi-repo
borg create "$BORG_REPO::$(hostname)-$(date +%Y-%m-%dT%H:%M)" /etc /home

Use key-based SSH authentication and restrict the backup user’s SSH access to Borg operations only by adding a command= restriction to authorized_keys on the remote host:

# On the remote host, in ~/.ssh/authorized_keys:
command="borg serve --restrict-to-path /backup/pi-repo" ssh-rsa AAAA...

For cloud storage, use rclone to sync the local Borg repository to an S3-compatible service after each backup run. The adjacent article Rclone on Raspberry Pi: Encrypt and Sync to Cloud covers rclone setup and encryption in detail. For a NAS target via SMB or NFS, see Time Machine Raspberry Pi NAS with Samba.

Maintenance

Repository integrity checks

export BORG_PASSCOMMAND='cat /etc/borg-passphrase'

# Weekly: check repository structure and index
borg check /mnt/backup/borg

# Monthly: add --verify-data to check every stored chunk
borg check --verify-data /mnt/backup/borg

borg check without --verify-data verifies the repository index and archive metadata but does not read and verify every data chunk. --verify-data reads the full dataset and confirms nothing has been silently corrupted. It is significantly slower on large repositories.

Monitor disk space

# Check free space on backup drive
df -h /mnt/backup

# Check repository size breakdown
borg info /mnt/backup/borg

Add a disk space check to the backup script that aborts if free space falls below a threshold:

FREE_GB=$(df --output=avail -BG /mnt/backup | tail -1 | tr -d 'G ')
if [ "$FREE_GB" -lt 10 ]; then
  echo "ERROR: Less than 10GB free on backup drive. Aborting."
  exit 1
fi

Handling a locked repository

# Check if any borg process is running before breaking the lock
ps aux | grep borg

# Break the lock only if no borg process is active
borg break-lock /mnt/backup/borg

A repository lock left by a crashed or interrupted backup prevents the next run from starting. Breaking the lock while another Borg process is actively writing will corrupt the repository. Only run borg break-lock when you have confirmed no Borg process is running.

Troubleshooting

Backup fails with permission denied

Borg cannot back up files it cannot read. Run the backup script as root if system directories like /etc and /var are included. For non-root scripts, grant the backup user read access to the required directories. Test as the user that cron or systemd will use before scheduling:

sudo -u backupuser /usr/local/bin/borg-backup.sh

Repository corruption

# Attempt repair (read all output carefully)
borg check --repair /mnt/backup/borg

# If a specific archive is corrupt, delete only that archive
borg delete /mnt/backup/borg::corrupt-archive-name

# Then re-run the backup to create a fresh archive

SSH key issues with remote backups

# Test SSH connection to backup host manually
ssh -i ~/.ssh/borg_key user@backup-host "borg --version"

# Use a named SSH host in ~/.ssh/config for cleaner scripts
# Host backup-host
#   HostName 192.168.1.x
#   User backupuser
#   IdentityFile ~/.ssh/borg_key

FAQ

How often should I run BorgBackup on Raspberry Pi?

Daily is appropriate for most Pi setups. For systems where data changes frequently, twice-daily archives are practical given Borg’s deduplication. For near-static systems like a media server, weekly is sufficient. The prune policy controls retention independently of backup frequency.

Can I back up multiple Pis to the same repository?

Yes. Use a consistent archive naming convention that includes the hostname, such as {hostname}-{now}. When pruning, use the --glob-archives flag to limit prune operations to archives from one host at a time:

borg prune --glob-archives 'pi4-*' --keep-daily=7 /mnt/backup/borg

What happens if I forget the Borg passphrase?

All encrypted archives become permanently unreadable. There is no recovery path. Store the passphrase in a password manager and keep the key export file (borg key export) in secure offline storage. Both are required for recovery if the repository is moved to a new host.

Does pruning delete all my backups?

No. borg prune removes only archives that fall outside the retention policy. Archives within the policy window are untouched. Use --dry-run to preview exactly which archives would be removed before running the actual prune. Deduplication means that removing an archive only frees space for chunks that are not referenced by any other retained archive.

What is the difference between BorgBackup and rsync?

rsync copies files efficiently by skipping unchanged data but does not provide versioned archives, deduplication across backups, or built-in encryption. Borg provides all three. rsync is appropriate for simple mirroring. Borg is appropriate when you need versioned point-in-time archives with storage efficiency and encryption.

References


About the Author

Chuck Wilson has been programming and building with computers since the Tandy 1000 era. His professional background includes CAD drafting, manufacturing line programming, and custom computer design. He runs PidiyLab in retirement, documenting Raspberry Pi and homelab projects that he actually deploys and maintains on real hardware. Every article on this site reflects hands-on testing on specific hardware and OS versions, not theoretical walkthroughs.

Last tested hardware: Raspberry Pi 4 Model B (4GB), USB SSD backup target. Last tested OS: Raspberry Pi OS Bookworm Lite 64-bit. BorgBackup 1.2.7.