BorgBackup on FreeBSD: Deduplicating Backup Review
BorgBackup (Borg) is a deduplicating backup program that stores only unique data chunks across all backups in a repository. If you back up a 50 GB dataset daily and only 500 MB changes each day, Borg stores 50 GB plus incremental deltas -- not 50 GB times the number of backups. This deduplication, combined with optional compression and authenticated encryption, makes Borg one of the most storage-efficient backup tools available. On FreeBSD, Borg runs as a Python-based command-line tool that understands Unix permissions, extended attributes, and file flags. It integrates well with ZFS snapshots for consistent backup sources and with cron/periodic for automated scheduling. This review covers Borg's architecture, FreeBSD installation and configuration, deduplication mechanics, encryption options, pruning strategies, remote repository setup, and how it compares with Restic and Tarsnap.
What BorgBackup Does
Borg is a file-level backup tool that stores files in a content-addressed repository. Each backup creates an "archive" -- a named snapshot of a set of files and directories at a point in time. Borg breaks files into variable-size chunks, deduplicates them against all previous chunks in the repository, optionally compresses them, and stores them with authenticated encryption.
Core capabilities:
- Deduplication -- content-defined chunking with a rolling hash (Buzhash). Identical data is stored once, regardless of which files or archives contain it.
- Compression -- LZ4 (fast), zstd (balanced), zlib (good ratio), and LZMA (maximum compression). Selectable per-archive or per-file-type.
- Authenticated encryption -- AES-256-CTR with HMAC-SHA256 (repokey mode) or the newer blake2b-based AEAD modes. The repository server never sees plaintext data.
- Pruning -- automatic deletion of old archives based on retention rules (keep last N daily, weekly, monthly, yearly backups).
- Remote repositories -- back up to a remote server over SSH. The remote server runs
borg serveand only needs Borg installed -- no special server software. - Mounting archives -- mount any archive as a FUSE filesystem for browsing and selective file recovery.
- Append-only mode -- repository can be configured so clients can only add new archives, not delete existing ones. Protects against ransomware and compromised clients.
- Verification --
borg checkverifies repository and archive integrity.
Borg does not provide continuous data protection (CDP), real-time replication, or block-level backup. It is a periodic, file-level backup tool designed for daily or hourly scheduled runs.
Installation on FreeBSD
Binary Package Installation
shpkg install py311-borgbackup
This installs Borg and its Python dependencies. The binary is accessible as borg:
shborg --version
Ports Installation
For custom build options (specific Python version, optional dependencies):
shcd /usr/ports/archivers/py-borgbackup make install clean
Optional: FUSE for Mount Support
To mount Borg archives as a filesystem for browsing:
shpkg install fusefs-libs3 kldload fusefs sysrc kld_list+=" fusefs"
Repository Setup
Initialize a Local Repository
shborg init --encryption=repokey /backup/borg-repo
This creates an encrypted repository at /backup/borg-repo. The encryption key is stored in the repository itself, protected by a passphrase. You will be prompted to set the passphrase.
Back up the key immediately:
shborg key export /backup/borg-repo /root/borg-key-backup.txt
Store this key export somewhere safe and offline. Without it and the passphrase, the repository is unrecoverable.
Encryption Modes
repokey-- encryption key stored in the repository, protected by passphrase. Most common choice.keyfile-- encryption key stored in~/.config/borg/keys/. More secure (key not on the repository server) but requires key backup.repokey-blake2-- same as repokey but uses BLAKE2b instead of SHA-256. Faster on modern CPUs.none-- no encryption. Only use for local repositories on encrypted filesystems (ZFS encryption, GELI).authenticated-- no encryption but data integrity verification. Use when encryption is handled at the storage layer.
For most FreeBSD deployments, repokey-blake2 is the recommended choice:
shborg init --encryption=repokey-blake2 /backup/borg-repo
Remote Repository
Initialize a repository on a remote server:
shborg init --encryption=repokey-blake2 ssh://backup-server/backup/borg-repos/freebsd-server
The remote server needs Borg installed. The connection uses SSH, so standard SSH key authentication applies.
Creating Backups
Basic Backup
shborg create /backup/borg-repo::daily-$(date +%Y%m%d-%H%M) \ /etc \ /usr/local/etc \ /home \ /root \ /var/db/mysql \ --exclude '/home/*/.cache' \ --exclude '*.tmp' \ --exclude '/var/db/mysql/*.pid'
This creates an archive named daily-20260409-0200 containing the specified paths, excluding cache directories and temporary files.
Compression Options
sh# Fast compression (default for daily backups) borg create --compression lz4 /backup/borg-repo::daily-$(date +%Y%m%d) /etc /home # Balanced compression (good for weekly/monthly) borg create --compression zstd,3 /backup/borg-repo::weekly-$(date +%Y%m%d) /etc /home # Maximum compression (for archival) borg create --compression lzma,6 /backup/borg-repo::archive-$(date +%Y%m%d) /etc /home
LZ4 adds negligible CPU overhead and achieves 2:1 compression on typical data. Zstd at level 3 achieves 3:1 with moderate CPU usage. LZMA achieves 4:1+ but is slow -- use it only for long-term archives.
Backup with Progress and Statistics
shborg create --stats --progress /backup/borg-repo::daily-$(date +%Y%m%d) /etc /home
The --stats flag prints deduplication statistics after completion, showing how much new data was stored versus how much was deduplicated.
Automated Backup Script
Create a comprehensive backup script for FreeBSD:
shcat > /usr/local/bin/borg-backup.sh << 'SCRIPT' #!/bin/sh export BORG_REPO="ssh://backup-server/backup/borg-repos/freebsd-server" export BORG_PASSPHRASE="your_passphrase_here" ARCHIVE_NAME="$(hostname)-$(date +%Y%m%d-%H%M%S)" LOG="/var/log/borg-backup.log" echo "Starting backup: ${ARCHIVE_NAME}" >> "$LOG" # Create backup borg create \ --stats \ --compression zstd,3 \ --exclude '/home/*/.cache' \ --exclude '/var/tmp/*' \ --exclude '/tmp/*' \ --exclude '*.core' \ "${BORG_REPO}::${ARCHIVE_NAME}" \ /etc \ /usr/local/etc \ /home \ /root \ /var/db \ /var/mail \ >> "$LOG" 2>&1 BACKUP_STATUS=$? # Prune old backups borg prune \ --keep-daily 7 \ --keep-weekly 4 \ --keep-monthly 6 \ --keep-yearly 2 \ "$BORG_REPO" \ >> "$LOG" 2>&1 # Compact repository (free disk space from pruned archives) borg compact "$BORG_REPO" >> "$LOG" 2>&1 echo "Backup completed with status: ${BACKUP_STATUS}" >> "$LOG" if [ $BACKUP_STATUS -ne 0 ]; then echo "Borg backup failed on $(hostname)" | mail -s "Backup FAILED" admin@example.com fi SCRIPT chmod 700 /usr/local/bin/borg-backup.sh
Schedule with cron:
shcrontab -e # Add: 0 2 * * * /usr/local/bin/borg-backup.sh
Deduplication Mechanics
Borg's deduplication is the core of its storage efficiency. Understanding how it works helps you optimize backups.
Content-Defined Chunking
Borg splits files into variable-size chunks using a rolling hash (Buzhash). Chunk boundaries are determined by the file content, not by fixed offsets. This means:
- Inserting data at the beginning of a file shifts all fixed-offset boundaries, causing a non-deduplicating backup tool to re-store the entire file. Borg re-stores only the new chunk at the insertion point.
- Files with identical content in different paths or archives are stored once.
- Small changes to large files result in only the changed chunks being stored.
Deduplication Ratio
Check your repository's deduplication efficiency:
shborg info /backup/borg-repo
This shows total original size, deduplicated size, and compression ratio. A healthy repository with daily backups of changing data typically shows 5:1 to 20:1 deduplication ratios after a few weeks.
Chunk Size Tuning
The default chunk size parameters work well for most workloads. For repositories backing up many large files (databases, virtual machine images), larger chunks reduce metadata overhead:
shborg init --chunker-params=19,23,21,4095 --encryption=repokey-blake2 /backup/borg-repo
This increases average chunk size from ~2 MB to ~4 MB. Do not change chunker parameters on an existing repository.
Pruning and Retention
Pruning removes old archives based on retention rules. It does not immediately free disk space -- that requires a separate compact step (Borg 1.2+).
Retention Strategy
shborg prune \ --keep-daily 7 \ --keep-weekly 4 \ --keep-monthly 12 \ --keep-yearly 5 \ /backup/borg-repo
This keeps:
- The last 7 daily backups.
- 4 weekly backups (one per week, going back 4 weeks).
- 12 monthly backups (one per month, going back 12 months).
- 5 yearly backups (one per year, going back 5 years).
Compact After Pruning
shborg compact /backup/borg-repo
This frees disk space occupied by chunks that are no longer referenced by any archive.
Dry Run
Test pruning before executing:
shborg prune --dry-run --list --keep-daily 7 --keep-weekly 4 /backup/borg-repo
The --list flag shows which archives would be kept and which would be pruned.
Restoring from Backups
List Archives
shborg list /backup/borg-repo
List Files in an Archive
shborg list /backup/borg-repo::daily-20260409-0200
Restore Specific Files
shcd / borg extract /backup/borg-repo::daily-20260409-0200 etc/pf.conf etc/rc.conf
This restores etc/pf.conf and etc/rc.conf to their original paths relative to the current directory.
Restore Everything
shcd / borg extract /backup/borg-repo::daily-20260409-0200
Mount and Browse
shmkdir /mnt/borg borg mount /backup/borg-repo::daily-20260409-0200 /mnt/borg ls /mnt/borg/etc/ cp /mnt/borg/home/user/important-file.txt /home/user/ borg umount /mnt/borg
FUSE mounting is the most convenient way to browse backups and selectively restore files.
ZFS Integration
On FreeBSD, combining Borg with ZFS snapshots provides the most consistent backup strategy.
Pre-Backup ZFS Snapshot
sh# Create consistent snapshots zfs snapshot zroot/var/db/mysql@borg-pre-backup # Mount snapshot read-only mkdir -p /mnt/borg-source/mysql mount -t zfs zroot/var/db/mysql@borg-pre-backup /mnt/borg-source/mysql # Back up from snapshot (consistent state) borg create /backup/borg-repo::daily-$(date +%Y%m%d) \ /etc /home /mnt/borg-source/mysql # Cleanup umount /mnt/borg-source/mysql zfs destroy zroot/var/db/mysql@borg-pre-backup
This ensures the database files are in a consistent state during the backup, avoiding corruption from files changing mid-backup.
Append-Only Mode
Protect against ransomware or compromised clients by configuring the remote repository in append-only mode:
On the backup server, create a restricted SSH key in ~/.ssh/authorized_keys:
shcommand="borg serve --append-only --restrict-to-path /backup/borg-repos/freebsd-server",restrict ssh-ed25519 AAAA... client-key
With append-only mode, the client can create new archives but cannot delete existing ones. An administrator on the backup server can prune old archives manually.
BorgBackup vs Restic
Restic is Borg's most direct competitor. Both use content-defined chunking and deduplication.
Choose Borg when:
- You want the most mature deduplicating backup tool with the longest track record.
- You need append-only repositories for ransomware protection.
- You prefer local or SSH-based remote storage.
- You want FUSE mounting for browsing archives.
- You need fine-grained compression control (per-archive or per-type).
Choose Restic when:
- You need native cloud storage support (S3, B2, Azure, GCS) without additional tools.
- You want a single static binary with no runtime dependencies.
- You need to back up to multiple backend types from the same tool.
- You prefer Go's deployment simplicity over Python's dependency management.
Both are excellent. The practical difference often comes down to storage backend: Borg for SSH-based storage, Restic for cloud object storage.
BorgBackup vs Tarsnap
Tarsnap is a cloud backup service by Colin Percival (FreeBSD Security Officer Emeritus). The comparison:
- Storage -- Tarsnap stores data on Tarsnap's servers (AWS-based). Borg stores data wherever you point it. Tarsnap is a service; Borg is a tool.
- Cost -- Tarsnap charges per byte stored and transferred. Borg has no recurring cost beyond your own storage.
- Encryption -- both provide client-side encryption. Tarsnap's encryption is handled by the client, and Tarsnap cannot read your data.
- Deduplication -- both deduplicate. Tarsnap deduplicates server-side as well, which can reduce costs.
- Trust model -- Tarsnap requires trusting a third-party service. Borg with your own infrastructure requires trusting only yourself.
Tarsnap is ideal for small, critical datasets (server configurations, encryption keys, small databases) where the convenience of a managed service outweighs the cost. Borg is better for large datasets where storage costs would be prohibitive on Tarsnap.
Monitoring
Check Repository Integrity
shborg check /backup/borg-repo
Run this monthly. It verifies the integrity of the repository data structures and all stored chunks.
Check a Specific Archive
shborg check --archives-only --last 5 /backup/borg-repo
Repository Info
shborg info /backup/borg-repo
Shows total size, deduplicated size, number of archives, and encryption mode.
Verdict
BorgBackup is the best file-level backup tool for FreeBSD systems that store backups on local disks or remote SSH servers. Its deduplication reduces storage costs dramatically for daily backups, its encryption protects data in transit and at rest, and its pruning system automates retention management. On FreeBSD, Borg pairs naturally with ZFS snapshots for consistent database backups and with cron for automated scheduling.
The main limitation is the lack of native cloud storage support -- if you need to back up directly to S3 or B2, Restic is the better choice. For SSH-based backup to your own infrastructure, Borg is the gold standard.
Frequently Asked Questions
How do I back up a FreeBSD jail?
Back up the jail's filesystem path directly: borg create repo::jail-daily /usr/local/jails/myjail. For running jails, take a ZFS snapshot first to ensure consistency.
What happens if a backup is interrupted?
Borg uses a transaction-based approach. Interrupted backups leave the repository in a consistent state -- the incomplete archive is discarded, and the repository remains valid. Re-run the backup to create a complete archive.
How much storage does Borg need?
For the first backup, roughly equal to the source data (minus compression savings). Subsequent backups store only changed chunks. A typical server with 50 GB of data and daily backups uses 55-60 GB after a month with good deduplication.
Can I use Borg without encryption?
Yes: borg init --encryption=none /backup/borg-repo. Use this only if the storage is already encrypted (ZFS encryption, GELI, encrypted cloud storage).
How do I migrate a Borg repository to a new server?
Copy the entire repository directory to the new server with rsync -a. The repository is self-contained. Update the backup script to point to the new location.
Does Borg support Windows?
No. Borg is Unix-only (FreeBSD, Linux, macOS). For cross-platform backups, use Restic.