Install BorgBackup on VPS for deduplication backup

Install BorgBackup on VPS: Encrypted Deduplication Backup

August 2026 · 9 min read

BorgBackup (Borg) is a Linux backup tool built around deduplication — identical data chunks are stored only once, regardless of how many backup archives contain them. This dramatically reduces storage requirements when keeping multiple versions. Key advantages include built-in AES-256 encryption, multiple compression options (lz4/zstd/zlib/lzma), and native SSH remote backup. Unlike rsync, Borg stores each backup run as a named archive that can be browsed and restored individually, or even mounted as a FUSE filesystem.

BorgBackup vs Other Backup Tools

FeatureBorgBackupResticrsync
Deduplication✅ Block-level✅ Content-defined❌ None
Encryption✅ Built-in (AES-256)✅ Built-in❌ Needs SFTP
Compression✅ lz4/zstd/zlib/lzma✅ zstd/zlib❌ None
Remote SSH✅ Native✅ Via plugin✅ Native
FUSE Mount✅ Easy✅ Available❌ None
SpeedVery fast (incremental)FastFast

Install BorgBackup on Ubuntu/Debian

# Update package list
sudo apt update

# Install BorgBackup
sudo apt install -y borgbackup

# Verify installed version
borg --version
# borg 1.2.x or newer

To get the latest version, install via pip or download the standalone binary:

# Install via pip
pip3 install borgbackup

# Or download the standalone binary from GitHub
wget https://github.com/borgbackup/borg/releases/latest/download/borg-linux64 -O /usr/local/bin/borg
chmod +x /usr/local/bin/borg

Initialize a Repository

A repository is the directory where Borg stores all archives. You must initialize it before first use:

# Create a local repository
borg init --encryption=repokey /backup/borg-repo

# Create a remote repository over SSH
borg init --encryption=repokey user@backup-server:/home/user/borg-repo

# You will be prompted to set a passphrase — make it strong and back it up!
Tip: --encryption=repokey stores the key inside the repository (convenient but requires backing up the passphrase). Use --encryption=keyfile to store the key separately for higher security.

Create Your First Backup Archive

# Basic syntax: borg create REPO::ARCHIVE_NAME PATHS...
borg create /backup/borg-repo::backup-{now:%Y-%m-%d} /home /etc /var/www

# Recommended options for production use
borg create \
  --verbose \
  --filter AME \
  --list \
  --stats \
  --show-rc \
  --compression lz4 \
  --exclude-caches \
  --exclude '/home/*/.cache' \
  --exclude '/home/*/.local/share/Trash' \
  /backup/borg-repo::backup-{now:%Y-%m-%dT%H:%M:%S} \
  /home \
  /etc \
  /var/www

After completion, Borg shows deduplication statistics:

------------------------------------------------------------------------------
Archive name: backup-2026-08-12T09:00:00
Time (start): Tue, 2026-08-12 09:00:00
Time (end):   Tue, 2026-08-12 09:02:15
Duration: 2 minutes 15 seconds
------------------------------------------------------------------------------
                       Original size      Compressed size    Deduplicated size
This archive:               12.50 GB              8.21 GB            1.23 GB
All archives:               37.50 GB             24.63 GB            2.41 GB
------------------------------------------------------------------------------

Prune Old Archives to Prevent Disk Overflow

# Keep: 7 daily, 4 weekly, 6 monthly, 1 yearly
borg prune \
  --list \
  --prefix 'backup-' \
  --show-rc \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 6 \
  --keep-yearly 1 \
  /backup/borg-repo

# Compact to actually reclaim disk space (Borg 1.2+)
borg compact /backup/borg-repo

# List remaining archives
borg list /backup/borg-repo

Restore Files from a Backup

# List all archives in repository
borg list /backup/borg-repo

# Browse contents of a specific archive
borg list /backup/borg-repo::backup-2026-08-12T09:00:00

# Restore entire archive to /tmp/restore
mkdir -p /tmp/restore
cd /tmp/restore
borg extract /backup/borg-repo::backup-2026-08-12T09:00:00

# Restore only specific path
borg extract /backup/borg-repo::backup-2026-08-12T09:00:00 home/user/Documents

# Mount archive as FUSE filesystem (interactive browsing)
sudo apt install -y python3-fuse
borg mount /backup/borg-repo::backup-2026-08-12T09:00:00 /mnt/borg-restore
ls /mnt/borg-restore/
# Unmount when done
borg umount /mnt/borg-restore

Automated Backup with Script and Cron

Create a script that handles create + prune + compact in one run:

sudo nano /usr/local/bin/borg-backup.sh
#!/usr/bin/env bash
set -euo pipefail

export BORG_REPO='/backup/borg-repo'
export BORG_PASSPHRASE='your-strong-passphrase-here'

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

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

borg prune \
  --list --prefix '{hostname}-' --show-rc \
  --keep-daily 7 --keep-weekly 4 --keep-monthly 6 \
  ::
PRUNE_RC=$?

borg compact ::

echo "=== Done: create=$CREATE_RC prune=$PRUNE_RC ==="
exit $(( CREATE_RC + PRUNE_RC ))
# Make executable and test
sudo chmod +x /usr/local/bin/borg-backup.sh
sudo /usr/local/bin/borg-backup.sh

# Schedule via cron (daily at 2:00 AM)
sudo crontab -e
# Add this line:
# 0 2 * * * /usr/local/bin/borg-backup.sh

Remote Backup over SSH

# Set up SSH key authentication (no password prompts)
ssh-keygen -t ed25519 -C "borg-backup" -f ~/.ssh/id_borg
ssh-copy-id -i ~/.ssh/id_borg.pub user@backup-server

# Initialize remote repository
borg init \
  --encryption=repokey \
  user@backup-server:/backup/borg-myserver

# Backup to remote (set in script)
export BORG_REPO='user@backup-server:/backup/borg-myserver'
export BORG_RSH='ssh -i ~/.ssh/id_borg'
borg create --compression lz4 ::'backup-{now:%Y-%m-%dT%H:%M:%S}' /home /etc

Verify BorgBackup is Working Correctly

# List all archives with sizes
borg list /backup/borg-repo

# Check repository integrity (metadata only, fast)
borg check /backup/borg-repo

# Full data verification (verifies actual stored data, slower)
borg check --verify-data /backup/borg-repo

# Show repository statistics
borg info /backup/borg-repo

# Check recent backup log
tail -50 /var/log/borg-backup.log

# Test-restore a single file to confirm restore works
cd /tmp
borg extract /backup/borg-repo::$(borg list /backup/borg-repo --last 1 --format '{archive}') etc/hostname
cat /tmp/etc/hostname
Tip: Schedule borg check /backup/borg-repo weekly and borg check --verify-data monthly. A backup that has never been verified may fail to restore exactly when you need it most — this is the most common backup mistake.

Troubleshooting Common Errors

Error: "passphrase supplied in BORG_PASSPHRASE is incorrect"

The passphrase in your environment variable does not match what was set during borg init. Watch for shell escaping issues with special characters like !, $, and \.

# Test passphrase directly with proper quoting
BORG_PASSPHRASE='your-passphrase' borg list /backup/borg-repo

# If passphrase contains single quotes, use double quotes
BORG_PASSPHRASE="your'passphrase" borg list /backup/borg-repo

Error: "Failed to create/acquire the lock"

A previous backup process crashed without releasing the lock. Verify no Borg process is currently running before breaking the lock.

# Check for running borg processes
ps aux | grep borg

# Break stale lock (only when certain no backup is running)
borg break-lock /backup/borg-repo

Error: "Repository already exists" during init

The target directory already contains a Borg repository. Check its contents first before deciding whether to reuse or recreate it.

# Check existing repository contents
borg list /backup/borg-repo

# Only if you truly want to start fresh (all existing archives will be lost!)
rm -rf /backup/borg-repo
borg init --encryption=repokey /backup/borg-repo

Error: "disk quota exceeded" on remote server

The remote server is out of space. Run prune to remove old archives, then compact to reclaim the freed space.

# Prune old archives
borg prune --keep-daily 3 --keep-weekly 2 /backup/borg-repo

# Compact to actually reclaim space
borg compact /backup/borg-repo

# Check remote disk usage
ssh user@backup-server "df -h /backup"

Error: "Connection closed by remote host" during backup

SSH session timeout during large backups. Add ServerAliveInterval to your SSH config or via the BORG_RSH variable.

# Add to ~/.ssh/config
Host backup-server
    ServerAliveInterval 60
    ServerAliveCountMax 10

# Or pass via BORG_RSH
export BORG_RSH='ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=10'

Frequently Asked Questions

Is BorgBackup suitable for a small VPS?

Absolutely. The first backup stores data at its full size, but subsequent backups only add blocks that have changed. A VPS with 20GB of data backed up daily for 30 days might use only 25-30GB of repository space, compared to 600GB for traditional full backups. The deduplication ratio improves over time as more archives accumulate.

Should I choose BorgBackup or Restic?

Both are excellent. Restic is written in Go, works on Windows/Mac/Linux, and natively supports S3, Backblaze B2, and Google Cloud Storage. BorgBackup is more mature, delivers higher deduplication efficiency, but runs only on Linux and macOS. For cloud storage backends, Restic has an edge. For Linux-to-Linux server backup over SSH, BorgBackup is the stronger choice.

Can I back up MySQL or PostgreSQL databases with BorgBackup?

Yes, but you must dump first. Backing up live database files risks getting a corrupt snapshot. Dump the database to a file, include that file in the Borg archive, then clean up:

# Example in backup script
mysqldump --all-databases | gzip > /tmp/mysql-backup.sql.gz
pg_dumpall | gzip > /tmp/postgres-backup.sql.gz

borg create ... /tmp/mysql-backup.sql.gz /tmp/postgres-backup.sql.gz /home /etc

# Remove dumps after backup completes
rm -f /tmp/mysql-backup.sql.gz /tmp/postgres-backup.sql.gz