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 pruneafter 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_PASSCOMMANDto read it at runtime. - Use
borg mountto browse an archive before extracting. This confirms the archive contains what you expect without unpacking the full dataset. - Run
borg checkweekly on active repositories. Add--verify-datamonthly 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.

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
| Feature | BorgBackup | rsync |
|---|---|---|
| Deduplication | Yes (chunk-level) | No |
| Encryption | Built-in | No (SSH transport only) |
| Compression | Yes (lz4, zstd, zlib) | No |
| Versioned archives | Yes | With --backup-dir workaround |
| Repository integrity check | borg check | No |
| Browse without extracting | borg mount | No |
| Remote backup | SSH native | SSH 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.
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:
| Mode | Key stored | Use case |
|---|---|---|
repokey | Inside repository | Local or trusted remote backup targets |
keyfile | On client machine | Untrusted remote targets (key never leaves client) |
repokey-blake2 | Inside repository | As above but faster MAC on modern hardware |
none | N/A | No 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.
| Flag | Keeps | Example |
|---|---|---|
--keep-daily | N most recent daily archives | --keep-daily=7 keeps one archive per day for 7 days |
--keep-weekly | N most recent weekly archives | --keep-weekly=4 keeps one per week for 4 weeks |
--keep-monthly | N most recent monthly archives | --keep-monthly=6 keeps one per month for 6 months |
--keep-yearly | N most recent yearly archives | --keep-yearly=1 keeps one per year |
--keep-within | All 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
- https://borgbackup.readthedocs.io/en/stable/
- https://borgbackup.readthedocs.io/en/stable/usage/prune.html
- https://www.freedesktop.org/software/systemd/man/systemd.timer.html
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.

