How to Install BorgBackup on Ubuntu 24.04 — Deduplicated Encrypted Backups
Ask any sysadmin who has been in the trade longer than five years what they would save from a burning datacentre, and the answer is never "the servers." It is "the backups." Production machines can be rebuilt from configuration management in an afternoon; a lost database is a lost business. This guide walks you through a production-grade BorgBackup deployment on Ubuntu 24.04, from apt install to a nightly, deduplicated, encrypted, verified backup stream pushing over SSH to a remote repository, orchestrated by borgmatic and wired into systemd timers.
Need a VPS to back up — or a cheap box to back up to? The CloudCore Starter plan gives you 100 GB of NVMe and a clean Ubuntu 24.04 image in under 60 seconds, which is enough to serve as either the protected workload or the remote Borg repository host.
Table of Contents
What is BorgBackup?
BorgBackup (usually just "Borg") is a deduplicating, compressing, authenticated-encryption backup program that has been the quiet workhorse of the Linux self-hosting world since 2015. It is a fork of the earlier Attic project, rewritten for better performance and long-term maintenance, and it is now packaged in every mainstream distribution including Ubuntu 24.04.
Borg's design is opinionated and the opinions have aged well:
- Content-defined chunking with Buzhash — files are split into variable-sized chunks (roughly 2 MiB by default) whose boundaries are chosen by a rolling hash, so inserting a single byte at the start of a 10 GB file does not invalidate every chunk. Only the chunks that actually changed get uploaded.
- Client-side encryption by default — the
repokeyandkeyfilemodes encrypt every chunk with AES-256-CTR and authenticate it with HMAC-SHA256 or BLAKE2b before it leaves your machine. The repository server never sees plaintext. - Append-only mode — a Borg repository can be locked into append-only semantics, which means a compromised client cannot delete old archives. This is the classic defence against ransomware that tries to wipe backups after encrypting the primary data.
- Push over SSH — a Borg repo lives on any SSH-reachable host with Borg installed. No S3 bucket, no object store, no third-party control plane. Your cheapest spare VPS becomes your backup target.
- Single repository, many clients — multiple machines can share one repo and deduplicate across each other. Back up fifty near-identical Nginx servers and you pay for roughly one set of binaries.
Why Borg vs. rsync, tar, or "just copy it"?
Plenty of VPS operators start with rsync -a /home /backup in a cron job and never revisit the decision. That works right up until the day it does not. Here is why Borg earns its install:
- Point-in-time snapshots.
rsyncmirrors the current state; delete a file at 10:00 and the 10:30 rsync happily deletes it on the backup. Borg keeps every nightly archive as a separate, restorable point in time. - Deduplication saves orders of magnitude of space. A tar-based "full + incremental" scheme either stores redundant data or chains fragile incrementals. Borg gives you full-snapshot semantics at incremental-storage cost.
- Encryption without a second tool. GPG-encrypting tarballs works but adds key management, scripting, and a failure mode where half your archive encrypts and half does not. Borg makes encryption the default path.
- Integrity verification is first-class.
borg checkreads every chunk and verifies the HMAC. Silent disk corruption on the backup target gets caught before a restore emergency. - Prune is a single command. Retention ("keep 7 daily, 4 weekly, 12 monthly") is a
borg pruneflag, not a bash loop withfind -mtime.
Prerequisites
Before you begin, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access (this is the client — the machine being backed up)
- A second SSH-reachable host to serve as the Borg repository — another Ubuntu VPS, a Hetzner Storage Box, rsync.net, BorgBase, or a physical NAS
- SSH key-based authentication configured between client and repo host (password-based Borg runs work but are a bad operational habit)
- At least 500 MB of free RAM on the client for the Borg process during create
- Disk headroom on the repo host of roughly 1.5x your largest single dataset — deduplication gives you compression on top, but plan for the uncompressed worst case
Recommended Plan: Starter>
For most single-server backup setups, CloudCore Starter is enough to act as either the protected workload or the remote Borg repository host:>
- 4 vCPU cores
- 8 GB RAM
- 100 GB NVMe SSD
- Unmetered bandwidth
- EUR 7.99/month>
Running Borg on the Starter plan for the source side leaves plenty of headroom for your real workload. Using a second Starter VPS in a different region as the repository side gives you geographic separation for under EUR 14/month total — cheaper than most managed backup services charge for a single server.
Connect to your client server via SSH to get started:
ssh root@your-client-ipStep 1: Update System Packages
Update the package index and install the latest security patches. Borg itself has a handful of Python and libacl dependencies, and the freshest versions in the Ubuntu 24.04 repository are the ones you want.
sudo apt update && sudo apt upgrade -yIf the kernel was updated, reboot before continuing:
sudo rebootReconnect after a minute and confirm you are on Ubuntu 24.04:
lsb_release -aExpected output:
Distributor ID: Ubuntu
Description: Ubuntu 24.04 LTS
Release: 24.04
Codename: nobleStep 2: Install BorgBackup
Ubuntu 24.04 ships BorgBackup 1.2.x in the main repository, which is the current stable branch and what borgmatic expects by default.
sudo apt install -y borgbackupVerify the install:
borg --versionExpected output:
borg 1.2.7You now have the borg binary in /usr/bin/borg. Do the same install on the repository host — Borg works by running a matching binary on both ends of the SSH connection, and a version mismatch across minor versions will be rejected.
If you need a newer Borg than Ubuntu ships (for example to use the 2.x preview), grab the single-file PyInstaller build from the project's release page and drop it in /usr/local/bin/borg. For this guide we stick with the apt package.
Step 3: Prepare the Remote Repository Host
On the repository host (the machine that will store the backups), create a dedicated unprivileged user whose sole job is to receive Borg pushes. This is both a security hygiene measure and a prerequisite for using the borg serve restriction mechanism.
SSH into the repo host:
ssh root@your-repo-host-ipCreate the user and home directory:
sudo useradd --create-home --shell /bin/bash borg
sudo mkdir -p /home/borg/.ssh
sudo chmod 700 /home/borg/.ssh
sudo touch /home/borg/.ssh/authorized_keys
sudo chmod 600 /home/borg/.ssh/authorized_keys
sudo chown -R borg:borg /home/borg/.sshBack on the client machine, generate an SSH key dedicated to Borg (do not reuse your personal key):
sudo ssh-keygen -t ed25519 -f /root/.ssh/borg_id_ed25519 -N ""
sudo cat /root/.ssh/borg_id_ed25519.pubCopy the printed public key. Back on the repo host, append it to the borg user's authorized_keys, wrapped with a command= restriction so the key can only run borg serve against a single directory:
sudo -u borg tee -a /home/borg/.ssh/authorized_keys <<'EOF'
command="borg serve --restrict-to-repository /home/borg/repos/client1",restrict ssh-ed25519 AAAA...yourkeyhere... borg@client1
EOFReplace AAAA...yourkeyhere... with the actual public key you copied. The restrict keyword disables port forwarding, X11, agent forwarding, and PTY allocation — everything except the one allowed command.
Create the repository directory:
sudo -u borg mkdir -p /home/borg/repos/client1Test the SSH connection from the client:
sudo ssh -i /root/.ssh/borg_id_ed25519 borg@your-repo-host-ipYou should get a message like Borg 1.2.7 starting in server mode and then disconnect. That proves the key restriction is working — you cannot get a shell, only a Borg serve session.
Step 4: Initialise the Encrypted Repository
Back on the client, initialise the repository. The repokey-blake2 mode stores the encryption key inside the repository itself, protected by your passphrase — convenient, but it means you must escrow the passphrase somewhere outside the backup chain.
First set a strong passphrase as an environment variable so it does not land in shell history:
sudo -i
read -rsp "Borg passphrase: " BORG_PASSPHRASE
export BORG_PASSPHRASE
export BORG_RSH='ssh -i /root/.ssh/borg_id_ed25519'
export BORG_REPO='ssh://borg@your-repo-host-ip/home/borg/repos/client1'Initialise with BLAKE2b-authenticated encryption:
borg init --encryption=repokey-blake2Expected output:
By default repositories initialized with this version will produce security errors if written to with an older version (up to and including Borg 1.0.8).If you want to use these older versions, you can disable the check by running: borg upgrade --disable-tam /home/borg/repos/client1
See https://borgbackup.readthedocs.io/en/stable/changes.html#pre-1-0-9-manifest-spoofing-vulnerability for details about the security implications.
No news is good news — an initialised repo prints these advisory lines and exits 0. Confirm it worked:
borg infoExpected output:
Repository ID: 7f3e...
Location: ssh://borg@your-repo-host-ip/home/borg/repos/client1
Encrypted: Yes (repokey BLAKE2b)
Cache: /root/.cache/borg/7f3e...
Security dir: /root/.config/borg/security/7f3e...
------------------------------------------------------------------------------
Original size Compressed size Deduplicated size
All archives: 0 B 0 B 0 BThe repository exists, it is encrypted, and it contains zero archives. Time to fix the last part.
Step 5: Back Up and Escrow the Key
This is the step that most first-time Borg users skip and regret. The key material stored inside a repokey repository is protected by your passphrase, but if the repository itself is destroyed (disk failure, accidental rm -rf, host wiped) you cannot decrypt the backups — which is obvious in retrospect and painful in practice.
Export the key to a portable ASCII file:
borg key export :: /root/borg-key-client1.txtPrint it and store it somewhere safe — a password manager's secure note field, an encrypted file on a USB stick you keep offline, or a printed paper in a safe (Borg key exports are deliberately short enough to print):
cat /root/borg-key-client1.txtStore the passphrase separately from the key file. If an attacker gets one without the other, they have nothing.
Now remove the plaintext key from the server:
shred -u /root/borg-key-client1.txtDo the same drill for the passphrase — your password manager is the correct home for it. A file on the same server being backed up is the wrong home.
Step 6: Run Your First Archive
With the repo initialised, create your first archive. A Borg archive is a named, point-in-time snapshot of whatever paths you tell it to include.
borg create --stats --progress --compression zstd,3 \
--exclude-caches \
--exclude '/home//.cache/' \
--exclude '/var/cache/*' \
--exclude '/var/tmp/*' \
--exclude '/tmp/*' \
::'{hostname}-{now:%Y-%m-%dT%H:%M:%S}' \
/etc /home /var/www /var/lib/mysql-backups /rootBreaking down the flags:
--stats— print deduplication and compression statistics at the end--progress— show a live progress line (drop this under cron)--compression zstd,3— zstandard compression at level 3 (the sweet spot of speed vs. ratio)--exclude-caches— skip directories tagged withCACHEDIR.TAG(Firefox, Chrome, various build tools)::'{hostname}-{now:...}'— the archive name, with placeholders expanded by Borg
------------------------------------------------------------------------------ Archive name: client1-2026-04-16T02:00:12 Archive fingerprint: 9a1d... Time (start): Thu, 2026-04-16 02:00:12 Time (end): Thu, 2026-04-16 02:04:33 Duration: 4 minutes 21.48 seconds Number of files: 48392 Utilization of max. archive size: 0% ------------------------------------------------------------------------------ Original size Compressed size Deduplicated size This archive: 3.42 GB 2.11 GB 2.05 GB All archives: 3.42 GB 2.11 GB 2.05 GB
Unique chunks Total chunks Chunk index: 14728 14892 ------------------------------------------------------------------------------
The first archive transfers everything. Run it a second time immediately and watch what deduplication actually means:
borg create --stats ::'{hostname}-{now:%Y-%m-%dT%H:%M:%S}' \
/etc /home /var/www /var/lib/mysql-backups /rootDeduplicated size for the second archive will be a few megabytes — the only new data is whatever changed between the two runs (log files, maybe).
List all archives:
borg listExpected output:
client1-2026-04-16T02:00:12 Thu, 2026-04-16 02:00:12 [9a1d...]
client1-2026-04-16T02:05:47 Thu, 2026-04-16 02:05:47 [b2e4...]Step 7: Install and Configure borgmatic
Running borg create by hand is fine for the first week. By week two you want a configuration file, a scheduler, a prune policy, pre-backup database dumps, and alerting on failure. That is borgmatic — a thin, well-behaved YAML wrapper over Borg.
Install it from apt:
sudo apt install -y borgmaticVerify:
borgmatic --versionExpected output:
1.8.9Generate a starter config:
sudo mkdir -p /etc/borgmatic
sudo borgmatic config generate --destination /etc/borgmatic/config.yamlEdit it:
sudo nano /etc/borgmatic/config.yamlA working config for our scenario looks like this (trimmed of comments):
source_directories: - /etc - /home - /var/www - /rootrepositories: - path: ssh://borg@your-repo-host-ip/home/borg/repos/client1 label: remote
exclude_patterns: - '/home//.cache/' - /var/cache/* - /var/tmp/* - /tmp/*
exclude_caches: true
archive_name_format: '{hostname}-{now:%Y-%m-%dT%H:%M:%S}'
compression: zstd,3
encryption_passcommand: cat /etc/borgmatic/passphrase ssh_command: ssh -i /root/.ssh/borg_id_ed25519
keep_daily: 7 keep_weekly: 4 keep_monthly: 12 keep_yearly: 2
checks: - name: repository frequency: 1 week - name: archives frequency: 1 month
postgresql_databases: - name: all format: custom
before_backup: - echo "Backup starting $(date)"
on_error: - 'curl -fsS -m 10 --retry 5 -o /dev/null https://hc-ping.com/YOUR-UUID/fail'
after_backup: - 'curl -fsS -m 10 --retry 5 -o /dev/null https://hc-ping.com/YOUR-UUID'
Create the passphrase file:
sudo mkdir -p /etc/borgmatic
echo 'your-strong-passphrase-here' | sudo tee /etc/borgmatic/passphrase > /dev/null
sudo chmod 600 /etc/borgmatic/passphrase
sudo chown root:root /etc/borgmatic/passphraseValidate the config:
sudo borgmatic config validateDo a dry run of the whole pipeline (create + prune + compact + check) without actually writing:
sudo borgmatic --dry-run --verbosity 1If the dry run passes, run it for real:
sudo borgmatic --verbosity 1borgmatic now handles the full lifecycle — dump databases, create the archive, prune old ones, compact the repo, and fire a webhook to Healthchecks.io on success or failure.
Step 8: Enable the systemd Timer
The borgmatic apt package ships a borgmatic.timer and borgmatic.service unit. Enable them instead of adding a cron line:
sudo systemctl enable --now borgmatic.timerVerify the timer is active and scheduled:
systemctl list-timers borgmatic.timerExpected output:
NEXT LEFT LAST PASSED UNIT ACTIVATES
Fri 2026-04-17 03:17:42 UTC 22h left Thu 2026-04-16 03:14:11 UTC 1h ago borgmatic.timer borgmatic.serviceThe default timer runs once a day with a randomised delay to avoid all servers hitting the repo host at the same minute. Override it if needed:
sudo systemctl edit borgmatic.timerAdd:
[Timer]
OnCalendar=
OnCalendar=--* 02:00:00
RandomizedDelaySec=30mWatch a run in real time:
sudo journalctl -u borgmatic.service -fStep 9: Prune and Check Policies
The keep_daily, keep_weekly, keep_monthly, keep_yearly keys in config.yaml control what borgmatic tells borg prune to keep. The 7/4/12/2 pattern from our config gives you:
- Every day from the last week
- One from each of the last 4 weeks
- One from each of the last 12 months
- One from each of the last 2 years
Tune these for your recovery point objective (RPO):
- High-churn database server:
keep_hourly: 24, keep_daily: 14, keep_weekly: 8, keep_monthly: 12— one year of retention, with hourly granularity for the last day. - Static documentation site:
keep_daily: 3, keep_weekly: 4, keep_monthly: 6— you almost never need hourly granularity and you are paying for storage you will not use. - Compliance-bound data:
keep_yearly: 7to match a 7-year retention requirement.
checks block schedules borg check runs:repositorycheck — validates the repository structure, cheap, weekly is reasonable.archivescheck — reads every chunk and verifies the HMAC, expensive for large repos, monthly is the usual cadence.
sudo borgmatic check --verbosity 1Expected output ends with:
Archive consistency check complete.
Verified integrity of /home/borg/repos/client1Step 10: Rehearse a Restore
Untested backups are not backups, they are hopes. Before you ever need a real restore, rehearse one on a scratch directory.
Option A: Extract a Single File
List archives to pick one:
sudo borg listList the contents of an archive:
sudo borg list ::client1-2026-04-16T02:00:12 | head -20Extract a specific file to the current directory:
cd /tmp/restore-test
sudo borg extract ::client1-2026-04-16T02:00:12 etc/nginx/nginx.conf
ls etc/nginx/nginx.confBorg recreates the relative path, so you get /tmp/restore-test/etc/nginx/nginx.conf.
Option B: Mount the Whole Archive
For browsing and cherry-picking, mount the archive as a FUSE filesystem:
sudo apt install -y fuse3
sudo mkdir -p /mnt/borg
sudo borg mount ::client1-2026-04-16T02:00:12 /mnt/borg
ls /mnt/borgNow /mnt/borg behaves like a read-only filesystem containing the archive. Copy what you need with cp, browse with your favourite file manager, or diff against the live filesystem. When you are done:
sudo borg umount /mnt/borgYou can also mount the entire repository (all archives simultaneously) by omitting the archive name:
sudo borg mount :: /mnt/borg
ls /mnt/borg # one directory per archiveOption C: Full Disaster Recovery
To simulate "the primary server is gone," rebuild a fresh Ubuntu 24.04 VPS, install borgbackup, copy the exported key file and passphrase from your escrow location, and extract the latest archive:
sudo apt install -y borgbackup export BORG_REPO='ssh://borg@your-repo-host-ip/home/borg/repos/client1' export BORG_RSH='ssh -i /root/.ssh/borg_id_ed25519' export BORG_PASSPHRASE='your-strong-passphrase-here'
sudo borg key import :: /root/borg-key-client1.txt sudo borg list sudo borg extract ::client1-2026-04-16T02:00:12
Everything lands in the current working directory with the original paths restored relative to it. rsync -a into the real locations, restart services, verify, done. If the full-DR rehearsal took more than an hour end-to-end, tune your setup until it does not — because when you are doing it for real, the clock matters.
Cost Comparison: BorgBase, Hetzner Storage Box, rsync.net, Self-Hosted
Borg runs against any SSH-reachable target, so the storage side is a straight price comparison. Typical pricing for 100 GB and 1 TB tiers (as of early 2026):
| Target | 100 GB | 1 TB | Extra features | Best for |
|---|---|---|---|---|
| BorgBase | ~$2/mo | ~$8/mo | Append-only, alerts, 2FA, Borg-native | Teams who want managed Borg without running a server |
| Hetzner Storage Box BX11 | ~EUR 3.45/mo | BX41 ~EUR 12.85/mo | SSH/SFTP/WebDAV, snapshots, 10 subaccounts | Price-sensitive admins comfortable with DIY |
| rsync.net | ~$1.50/mo (at 1 TB rate; 100 GB plan is $30/yr) | ~$18/mo | ZFS snapshots, 11 global locations, free Borg support | Regulated industries wanting audit-friendly storage |
| Self-hosted on CloudCore Starter | EUR 7.99/mo covers 100 GB | EUR 7.99/mo covers 100 GB; need larger plan for 1 TB | Full control, reusable for other workloads | Operators who already run a VPS fleet |
| Self-hosted on home NAS | "free" (sunk cost) | "free" | Physical control, zero bandwidth cost for LAN | Single-site setups with existing hardware |
- BorgBase and rsync.net both bill for the deduplicated size, which is what Borg actually stores. Hetzner bills for quota you buy, whether you use it or not — cheaper at the upper end if you are disciplined.
- Append-only mode is available on all four; BorgBase makes it a one-click toggle while the others want you to configure the
--append-onlyflag in the server-side Borg invocation. - Bandwidth on Hetzner Storage Boxes and CloudCore VPS is unmetered for reasonable use; rsync.net and BorgBase are storage-priced and do not charge egress. AWS S3, for comparison, charges egress separately, which is why Borg-over-S3-gateway setups rarely pencil out.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
Connection closed by remote host on init | SSH key restriction wrong or path outside --restrict-to-repository | Recheck the command="borg serve --restrict-to-repository ..." line in the repo user's authorized_keys; the path must match what the client requests exactly. |
Repository ... does not exist after init worked | Client and server Borg versions mismatch across major versions | Ensure both sides run the same Borg major version: borg --version on each. Upgrade the older one. |
Failed to create/acquire the lock | A previous backup run crashed and left a stale lock | borg break-lock :: on the client. Safe to run when no backup is active. |
Remote: borg: command not found | Borg not installed on the repository host, or not in the PATH that ssh non-interactive sessions see | sudo apt install borgbackup on the repo host, then test with ssh borg@repo "borg --version". |
| First archive takes many hours | Initial upload transfers all unique data, limited by bandwidth | Expected — subsequent archives deduplicate. Consider seeding the repo over LAN/physical before moving the target to its final location. |
borg check reports corrupted chunks | Storage corruption on the repository host | Restore the repo from its own backup (you back up your backup host, right?), or from a second Borg repository — this is why the 3-2-1 rule exists. |
| borgmatic timer runs but no archives appear | encryption_passcommand failing silently | sudo borgmatic --verbosity 2 manually; check /etc/borgmatic/passphrase is readable by root and contains only the passphrase with no trailing newline. |
Out-of-memory during borg create | Cache rebuild on a very large repo | Temporarily add swap: sudo fallocate -l 4G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile. Long-term, move to a larger plan. |
| Extremely slow restore | FUSE mount over SSH on a high-latency link | Use borg extract instead of borg mount for full restores; mount is fine for browsing but slow for bulk copy. |
Viewing logs
sudo journalctl -u borgmatic.service -n 100 --no-pagerFollow live:
sudo journalctl -u borgmatic.service -fFor one-off debugging, run borgmatic with maximum verbosity:
sudo borgmatic --verbosity 2 --syslog-verbosity 0FAQ
What is the difference between BorgBackup and borgmatic?
BorgBackup is the underlying binary that performs the deduplicated, encrypted backup to a local or remote repository. borgmatic is a Python wrapper that reads a single YAML configuration file and orchestrates borg create, borg prune, borg compact, and borg check in the right order, with database dump hooks, webhook notifications, and systemd integration. You always need Borg installed. borgmatic is optional but highly recommended because it replaces fragile shell scripts, bespoke cron wrappers, and the "wait, which version of the backup script is on this server?" problem.
Is BorgBackup encryption strong enough for regulated data?
Yes. Borg encrypts each chunk client-side with AES-256 in counter mode and authenticates it with HMAC-SHA256 or BLAKE2b. The repository owner (the SSH target host, the cloud provider, the managed-Borg vendor) cannot read your data — this is the property auditors look for under GDPR, HIPAA, PCI-DSS, and SOC 2 when storage is delegated to a third party. The critical operational rule is that you must back up both the passphrase and the key file to a location separate from the primary data and the backup repository. Losing either one makes the repository permanently unreadable — a property that is a feature when it stops attackers and a disaster when it stops you.
How does BorgBackup deduplication compare to ZFS or btrfs dedup?
Borg dedupes at the chunk level across archives, across servers sharing a repository, and across time, and it does so before any data touches the wire. ZFS and btrfs dedupe at the block level on the storage layer and only within a single pool — and ZFS dedup in particular has a reputation for high RAM cost. Borg's content-defined chunking (with variable chunk boundaries chosen by a rolling hash) also survives small insertions in large files, which fixed-block dedup does not. The two are complementary: you can absolutely store Borg repositories on a ZFS dataset with its own snapshots and compression, and many operators do exactly that.
Can I restore a single file without restoring the whole archive?
Yes. borg extract ::archive-name path/to/file pulls one file. borg mount ::archive-name /mnt/borg exposes the whole archive as a read-only FUSE filesystem you can browse with ls, cp, or a file manager. Mount is the preferred option when you are hunting for "that config from three weeks ago" because it turns restore-discovery into filesystem navigation.
How often should I run borg check?
Run a full borg check (repository plus archives) at least monthly. Run the cheaper repository-only check weekly. The full check reads every chunk and validates the HMAC, which is the mechanism that catches silent bit rot on the backup target before you discover it during a real restore. borgmatic schedules both checks independently from the create/prune cycle so you do not have to think about it after the first config.
Should I use BorgBackup or restic?
Both deduplicate, encrypt, and incrementalise. Choose Borg when your target is an SSH-accessible server and you want the most mature push-over-SSH tooling, battle-tested since 2015, with first-class append-only semantics and a rich ecosystem of wrappers like borgmatic and Vorta. Choose restic when your target is object storage (S3, B2, Wasabi) or when you want a single static Go binary with no Python runtime. Many operators run both — Borg to a cheap SSH box for daily, restic to S3 for weekly offsite — and call it a 3-2-1 done.
Does BorgBackup work for backing up databases?
Borg does not understand database internals, so you must dump databases to a file first and let Borg back up the dump. borgmatic has built-in database hooks for PostgreSQL, MySQL/MariaDB, MongoDB, and SQLite that run pg_dump/mysqldump before the archive and clean up after, giving you consistent point-in-time database snapshots without custom pre-hook scripts. The postgresql_databases and mysql_databases blocks in config.yaml are usually all you need.
Can multiple servers share one Borg repository?
Yes, and deduplication across servers is one of Borg's strongest wins. Fifty near-identical Ubuntu web servers pushing to the same repo pay for roughly one set of OS files plus each server's unique application data. The caveat is that only one client can write at a time (Borg uses a lock file), so stagger the systemd timers by a few minutes across servers, or use the --lock-wait flag to let late arrivals queue.
Next Steps
With Borg running nightly and verified, here are the natural follow-ons:
- Pair with a second tool for 3-2-1 compliance. Install restic pointed at S3 or B2 as a second, geographically separate backup stream. The 3-2-1 rule (3 copies, 2 media, 1 offsite) is satisfied when Borg lives on a VPS repo host and restic lives in object storage.
- Add monitoring. Wire the
on_errorandafter_backuphooks inconfig.yamlto Healthchecks.io, Uptime Kuma, or a Discord/Slack webhook. Silent backup failure is the worst backup failure.
- Protect user-facing data. Combine Borg with Nextcloud to give end users self-service file recovery on top of admin-driven system-level backups.
- Evaluate alternatives for specific workloads. If you need a GUI for non-technical operators, look at Duplicati. For a modern Go-based tool with a polished web UI, see Kopia. For classic hard-link-based rotations of a local filesystem, rsnapshot is still a valid and dead-simple choice.
- Install Vorta for desktop machines. Vorta is a Qt GUI for Borg that pairs well with borgmatic on servers — the same repository, queried by laptops and workstations, deduplicated across the fleet.
- Test the full DR drill quarterly. Put it on the calendar. Spin up a clean VPS, restore from Borg, verify services come up, document how long each step took, fix what was slow. The first drill will teach you more about your backup system than the first year of nightly archives.
Skip the manual install — get a backup-ready VPS>
Our CloudCore Starter plan gives you a clean Ubuntu 24.04 VPS in under 60 seconds, with the NVMe headroom and bandwidth to serve as either the protected workload or the remote Borg repository host. Run one for your application and one for your backups in a different region for geographic redundancy under EUR 14/month total.>
- 4 vCPU cores, 8 GB RAM, 100 GB NVMe
- Unmetered bandwidth
- Ubuntu 24.04 LTS ready to apt install borgbackup
- Multiple regions for geographic separation>
Deploy your backup VPS now — plans from EUR 7.99/month.