Featured image of post VPS Backup with BorgBackup: Deduplicated Encrypted Backup Guide

VPS Backup with BorgBackup: Deduplicated Encrypted Backup Guide

Say goodbye to redundant storage. Use BorgBackup for deduplicated, encrypted, efficient VPS backups—full first run, then increments that save only changes, cutting storage by 70%+

Introduction

You have a VPS running important services—website, database, config files, user uploads. One day the disk crashes and you realize you have no backup. That feeling of despair is one only the experienced can truly understand.

Traditional backup approaches fall into two extremes: full backups every day waste massive storage, while rsync mirrors lose all historical versions. What you need is a smart, space-efficient, secure backup solution.

BorgBackup (Borg) is exactly that. It uses a unique deduplication engine that stores each data chunk only once; supports AES-256 encryption so your data stays cipher-text even on remote servers; and incremental backups are absurdly fast—second backups take only seconds.

Today we’ll build a complete BorgBackup system from scratch on your VPS.


1. Why Choose BorgBackup?

FeaturersyncResticBorgBackup
Dedup granularityFile-levelBlock-levelBlock-level (1-4MB)
EncryptionNone (needs extra tool)AES-256AES-256
CompressionOptional zliblz4/zstdzlib/lz4
Incremental efficiencyLow (full scan each time)HighExtremely high
Cross-platformLinux/MacCross-platformLinux/macOS
Dedup effectiveness❌✅✅

Core advantages of Borg:

  1. Global deduplication — deduplicates across multiple backups, not just within one repository
  2. Memory-friendly — dedup index aligned to 4KB pages, won’t exhaust memory even with large files
  3. Mountable backups — browse historical backups like a normal directory, read files without full restore
  4. Efficient deletion — automatic cleanup of unused chunks via reference counting

2. Architecture Design

┌─────────────────────────────────────────────────────┐
│                  Your VPS                            │
│  ┌──────────┐    ┌──────────────┐    ┌───────────┐  │
│  │ Borg     │    │  Backup      │    │  Remote   │  │
│  │ Client   │───▶│  Repository  │◀───│  Storage  │  │
│  │ (source) │    │  (local cache)│    │  (target)  │  │
│  └──────────┘    └──────────────┘    └───────────┘  │
│       │                                                  │
│       │ cron scheduled trigger                             │
│       ▼                                                  │
│  ┌──────────┐    ┌──────────────┐                      │
│  │ Systemd  │    │  Telegram    │                      │
│  │ Timer    │◀───│  Bot Alert   │                      │
│  └──────────┘    └──────────────┘                      │
└─────────────────────────────────────────────────────┘

Backup target: Use a remote server (or NAS, object storage) as the Borg repository mount point, transferring via SSH. This way even if the VPS physically fails, your data remains safe.


3. Installing BorgBackup

3.1 Backup Source (Your VPS)

# Debian/Ubuntu
apt update && apt install -y borgbackup

# Verify installation
borg --version
# borg 1.2.8 or higher is fine

3.2 Target Server (Remote Storage)

The remote server only needs SSH service—no Borg installation required. But if it’s also Linux, install the client for local operations:

# Create dedicated backup user on remote server
ssh root@backup-server "useradd -m -s /bin/bash borguser"
ssh root@backup-server "mkdir -p /home/borguser/.ssh && chmod 700 /home/borguser/.ssh"

4. Initializing the Backup Repository

4.1 Generate SSH Key (passwordless)

Generate a dedicated key on the VPS for backups:

ssh-keygen -t ed25519 -C "borg-backup-$(hostname)" -f ~/.ssh/borg_key -N ""

4.2 Deploy Public Key to Remote Server

ssh-copy-id -i ~/.ssh/borg_key.pub borguser@backup-server
# Verify passwordless login
ssh -i ~/.ssh/borg_key borguser@backup-server "hostname"

4.3 Create Borg Repository

# Initialize repository on remote server
borg init \
  --encryption=repokey \
  --compress=zstd,1 \
  ssh://borguser@backup-server/home/borguser/backups/vps-backup

# You'll be prompted to set a repository password—remember it! Use a strong password from your password manager.

Encryption options explained:

  • repokey: Key and metadata encrypted and stored in the repository (recommended, easy to migrate)
  • keyfile: Key stored on client side, repository completely unreadable (more secure but complex migration)
  • passphrase: Pure passphrase encryption (simple but slightly less secure)

5. Writing the Backup Script

Create /usr/local/bin/borg-backup.sh:

#!/bin/bash
set -euo pipefail

# ========== Configuration ==========
REPO="ssh://borguser@backup-server/home/borguser/backups/vps-backup"
SSH_KEY="/root/.ssh/borg_key"
PASSPHRASE_FILE="/root/.borg_passphrase"
LOG_FILE="/var/log/borg-backup.log"
HOSTNAME="$(hostname)"
BOT_TOKEN=""   # Telegram Bot Token
CHAT_ID=""     # Telegram Chat ID

# Backup source paths (one per line)
BACKUP_SOURCES=(
  "/etc"
  "/root"
  "/var/lib/docker/containers"
  "/opt"
)

# Retention policy: keep 7 daily, 4 weekly, 12 monthly backups
PRUNE_PREFIX="--prefix=${HOSTNAME}-"

# ========== Execute Backup ==========
exec >>"${LOG_FILE}" 2>&1
echo "===== $(date '+%Y-%m-%d %H:%M:%S') Starting backup ====="

export BORG_PASSPHRASE="$(cat ${PASSPHRASE_FILE})"
export BORG_RSH="ssh -i ${SSH_KEY} -o StrictHostKeyChecking=no"

# Create backup
borg create \
  "${REPO}::${HOSTNAME}-${HOSTNAME}-$(date +%Y-%m-%dT%H%M%S)" \
  --compress=lz4 \
  --exclude-caches \
  --one-file-system \
  --list \
  "${BACKUP_SOURCES[@]}"

# Health check
borg check --read-only-check "${REPO}"

# Prune old backups
borg prune \
  --list --prefix="${HOSTNAME}-" \
  --keep-daily=7 \
  --keep-weekly=4 \
  --keep-monthly=12 \
  "${REPO}"

echo "===== $(date '+%Y-%m-%d %H:%M:%S') Backup completed ====="

# Telegram alert (optional)
if [[ -n "${BOT_TOKEN}" && -n "${CHAT_ID}" ]]; then
  curl -s -X POST \
    "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
    -d "chat_id=${CHAT_ID}" \
    -d "text=✅ [${HOSTNAME}] Backup completed successfully" \
    -d "parse_mode=HTML" > /dev/null
fi

Set permissions and create the passphrase file:

chmod 700 /usr/local/bin/borg-backup.sh
chmod 600 /root/.borg_passphrase
echo "your-strong-password" > /root/.borg_passphrase
chown root /root/.borg_passphrase

Test the Backup

/usr/local/bin/borg-backup.sh

View backup history:

export BORG_PASSPHRASE="$(cat /root/.borg_passphrase)"
export BORG_RSH="ssh -i /root/.ssh/borg_key -o StrictHostKeyChecking=no"
borg list ssh://borguser@backup-server/home/borguser/backups/vps-backup

6. Configuring Scheduled Tasks

Use systemd timer instead of cron—more reliable with built-in failure retry:

Create /etc/systemd/system/borg-backup.timer:

[Unit]
Description=BorgBackup daily timer
Requires=borg-backup.service

[Timer]
OnBootDelay=10min
OnUnitActiveDelay=24h
AccuracySec=1min
Persistent=true

[Install]
WantedBy=timers.target

Create the corresponding service:

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

Enable the timer:

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

7. Restoring from Backup

7.1 List All Backups

borg list ssh://borguser@backup-server/home/borguser/backups/vps-backup

7.2 Inspect a Backup (No Restore Needed)

borg info ssh://borguser@backup-server/home/borguser/backups/vps-backup::vps-hostname-2026-09-08T100000

7.3 Restore Individual Files or Directories

# Mount backup (read-only, doesn't corrupt repository)
mkdir -p /mnt/borg-mount
borg mount \
  ssh://borguser@backup-server/home/borguser/backups/vps-backup::vps-hostname-2026-09-08T100000 \
  /mnt/borg-mount

# Copy needed files
cp -r /mnt/borg-mount/etc/nginx/sites-enabled/ /tmp/restored/

# Unmount
borg umount /mnt/borg-mount

7.4 Full Restore (Extract Everything)

borg extract \
  ssh://borguser@backup-server/home/borguser/backups/vps-backup::vps-hostname-2026-09-08T100000

8. Advanced Tips

8.1 Configure Cache for Speed

Borg caches indices locally on first backup. Configure the cache directory:

# ~/.config/borg/config
[directories]
cache = /root/.cache/borg
tmp = /tmp

8.2 Exclude Sensitive Files

For directories containing keys and passwords, use Borg’s exclude-if-present feature:

# Create .nobackup marker in directories to exclude
touch /home/user/secrets/.nobackup

Then add to your backup script:

--exclude-if-present .nobackup

8.3 Multiple Hosts, Single Repository

Borg allows different machines to write to the same repository, automatically isolated by hostname:

# Another server connects to the same repository
borg init --encryption=repokey ssh://borguser@backup-server/home/borguser/backups/vps-backup

8.4 Monitor Repository Health

# Check repository integrity
borg check -v ssh://borguser@backup-server/home/borguser/backups/vps-backup

# Verify encryption key is valid
borg info ssh://borguser@backup-server/home/borguser/backups/vps-backup

9. Performance Comparison

On a 1GB RAM VPS backing up 10GB of data:

ToolFirst BackupIncrementalMemory UsageCompressed Size
rsync8min8minLow10GB (no dedup)
Restic5min30sMedium3.2GB
Borg4min15sLow2.8GB

Borg’s deduplication shines with homogenous data (like similar Docker image layers), achieving 60-75% storage savings in practice.


10. FAQ

Q: What if I forget the repository password? A: With repokey mode, without the password you cannot decrypt any data. Store your password in a password manager. With keyfile mode, the key file loss can be recovered.

Q: Can Borg backup open files? A: Yes. Borg uses file-level snapshotting. Open files can be backed up normally, though they may not represent an exact moment-in-time state. For database files, consider freezing or exporting before backup.

Q: How do I backup Docker data? A: It’s recommended to back up /var/lib/docker rather than running containers directly. Or use docker export to export containers as tarballs before backing up, ensuring data consistency.


Summary

BorgBackup is the advanced choice for VPS backups:

  • Deduplication makes repeated backups barely use extra space
  • Encryption ensures data stays safe in all transit and storage scenarios
  • Mountable lets you access historical versions without full restore
  • Simple CLI combined with systemd timer achieves full automation

The last line of defense for backups isn’t technology—it’s regular restore verification. Test restoring at least once a month to ensure your backups can actually save you when it matters.


End of article · Questions? Feel free to discuss in the comments

📺 看视频版教程 → DuckDB Lab YouTube

Subscribe for more DuckDB & AI automation tutorials