Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Borgbackup Ubuntu
GUIDEInstall Guides

How to Install BorgBackup on Ubuntu 24.04 — Deduplicated Encrypted Backups

30 min read

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?
  • Why Borg vs. rsync, tar, or "just copy it"?
  • Prerequisites
  • Step 1: Update System Packages
  • Step 2: Install BorgBackup
  • Step 3: Prepare the Remote Repository Host
  • Step 4: Initialise the Encrypted Repository
  • Step 5: Back Up and Escrow the Key
  • Step 6: Run Your First Archive
  • Step 7: Install and Configure borgmatic
  • Step 8: Enable the systemd Timer
  • Step 9: Prune and Check Policies
  • Step 10: Rehearse a Restore
  • Cost Comparison: BorgBase, Hetzner Storage Box, rsync.net, Self-Hosted
  • Troubleshooting
  • FAQ
  • Next Steps
  • 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 repokey and keyfile modes 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.
    The tool is written in Python with performance-critical bits in C, released under the 3-clause BSD licence, and maintained by a small but steady group of contributors. It is boring in the best sense: it does one thing, it does not pivot, and nightly backups from 2018 still restore cleanly today.

    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. rsync mirrors 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 check reads 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 prune flag, not a bash loop with find -mtime.
    If you are weighing alternatives, see our deep-dives on restic for an S3-first alternative, duplicati for a GUI-driven Windows-friendly option, kopia for a modern Go implementation with a web UI, and rsnapshot for the classic hard-link-based approach. For end-user-facing data specifically, pairing Borg with Nextcloud gives you user self-service restores on top of the admin-driven Borg layer.

    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:

    bash
    ssh root@your-client-ip

    Step 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.

    bash
    sudo apt update && sudo apt upgrade -y

    If the kernel was updated, reboot before continuing:

    bash
    sudo reboot

    Reconnect after a minute and confirm you are on Ubuntu 24.04:

    bash
    lsb_release -a

    Expected output:

    text
    Distributor ID: Ubuntu
    Description:    Ubuntu 24.04 LTS
    Release:        24.04
    Codename:       noble

    Step 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.

    bash
    sudo apt install -y borgbackup

    Verify the install:

    bash
    borg --version

    Expected output:

    text
    borg 1.2.7

    You 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:

    bash
    ssh root@your-repo-host-ip

    Create the user and home directory:

    bash
    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/.ssh

    Back on the client machine, generate an SSH key dedicated to Borg (do not reuse your personal key):

    bash
    sudo ssh-keygen -t ed25519 -f /root/.ssh/borg_id_ed25519 -N ""
    sudo cat /root/.ssh/borg_id_ed25519.pub

    Copy 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:

    bash
    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
    EOF

    Replace 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:

    bash
    sudo -u borg mkdir -p /home/borg/repos/client1

    Test the SSH connection from the client:

    bash
    sudo ssh -i /root/.ssh/borg_id_ed25519 borg@your-repo-host-ip

    You 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:

    bash
    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:

    bash
    borg init --encryption=repokey-blake2

    Expected output:

    text
    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:

    bash
    borg info

    Expected output:

    text
    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 B

    The 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:

    bash
    borg key export :: /root/borg-key-client1.txt

    Print 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):

    bash
    cat /root/borg-key-client1.txt

    Store 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:

    bash
    shred -u /root/borg-key-client1.txt

    Do 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.

    bash
    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 /root

    Breaking 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 with CACHEDIR.TAG (Firefox, Chrome, various build tools)
    • ::'{hostname}-{now:...}' — the archive name, with placeholders expanded by Borg
    Expected output (abbreviated):

    text
    ------------------------------------------------------------------------------
    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:

    bash
    borg create --stats ::'{hostname}-{now:%Y-%m-%dT%H:%M:%S}' \
      /etc /home /var/www /var/lib/mysql-backups /root

    Deduplicated 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:

    bash
    borg list

    Expected output:

    text
    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:

    bash
    sudo apt install -y borgmatic

    Verify:

    bash
    borgmatic --version

    Expected output:

    text
    1.8.9

    Generate a starter config:

    bash
    sudo mkdir -p /etc/borgmatic
    sudo borgmatic config generate --destination /etc/borgmatic/config.yaml

    Edit it:

    bash
    sudo nano /etc/borgmatic/config.yaml

    A working config for our scenario looks like this (trimmed of comments):

    yaml
    source_directories:
        - /etc
        - /home
        - /var/www
        - /root

    repositories: - 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:

    bash
    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/passphrase

    Validate the config:

    bash
    sudo borgmatic config validate

    Do a dry run of the whole pipeline (create + prune + compact + check) without actually writing:

    bash
    sudo borgmatic --dry-run --verbosity 1

    If the dry run passes, run it for real:

    bash
    sudo borgmatic --verbosity 1

    borgmatic 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:

    bash
    sudo systemctl enable --now borgmatic.timer

    Verify the timer is active and scheduled:

    bash
    systemctl list-timers borgmatic.timer

    Expected output:

    text
    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.service

    The 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:

    bash
    sudo systemctl edit borgmatic.timer

    Add:

    ini
    [Timer]
    OnCalendar=
    OnCalendar=--* 02:00:00
    RandomizedDelaySec=30m

    Watch a run in real time:

    bash
    sudo journalctl -u borgmatic.service -f

    Step 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
    At steady state that is roughly 7 + 4 + 12 + 2 = 25 archives, and because of deduplication the storage cost is far less than 25 times the dataset size.

    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: 7 to match a 7-year retention requirement.
    The checks block schedules borg check runs:

    • repository check — validates the repository structure, cheap, weekly is reasonable.
    • archives check — reads every chunk and verifies the HMAC, expensive for large repos, monthly is the usual cadence.
    Run the checks manually if you want to confirm the repository is healthy right now:

    bash
    sudo borgmatic check --verbosity 1

    Expected output ends with:

    text
    Archive consistency check complete.
    Verified integrity of /home/borg/repos/client1

    Step 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:

    bash
    sudo borg list

    List the contents of an archive:

    bash
    sudo borg list ::client1-2026-04-16T02:00:12 | head -20

    Extract a specific file to the current directory:

    bash
    cd /tmp/restore-test
    sudo borg extract ::client1-2026-04-16T02:00:12 etc/nginx/nginx.conf
    ls etc/nginx/nginx.conf

    Borg 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:

    bash
    sudo apt install -y fuse3
    sudo mkdir -p /mnt/borg
    sudo borg mount ::client1-2026-04-16T02:00:12 /mnt/borg
    ls /mnt/borg

    Now /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:

    bash
    sudo borg umount /mnt/borg

    You can also mount the entire repository (all archives simultaneously) by omitting the archive name:

    bash
    sudo borg mount :: /mnt/borg
    ls /mnt/borg   # one directory per archive

    Option 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:

    bash
    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):

    Target100 GB1 TBExtra featuresBest for
    BorgBase~$2/mo~$8/moAppend-only, alerts, 2FA, Borg-nativeTeams who want managed Borg without running a server
    Hetzner Storage Box BX11~EUR 3.45/moBX41 ~EUR 12.85/moSSH/SFTP/WebDAV, snapshots, 10 subaccountsPrice-sensitive admins comfortable with DIY
    rsync.net~$1.50/mo (at 1 TB rate; 100 GB plan is $30/yr)~$18/moZFS snapshots, 11 global locations, free Borg supportRegulated industries wanting audit-friendly storage
    Self-hosted on CloudCore StarterEUR 7.99/mo covers 100 GBEUR 7.99/mo covers 100 GB; need larger plan for 1 TBFull control, reusable for other workloadsOperators who already run a VPS fleet
    Self-hosted on home NAS"free" (sunk cost)"free"Physical control, zero bandwidth cost for LANSingle-site setups with existing hardware
    A few operational notes that do not fit in the table:

    • 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-only flag 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.
    For most readers of this guide, two CloudCore Starter VPS instances in different regions (client + repository host) is the cleanest answer: EUR 13.98/month, geographic redundancy, full ownership of the stack, and the repository host can double as a staging/monitoring box.

    Troubleshooting

    ProblemCauseSolution
    Connection closed by remote host on initSSH key restriction wrong or path outside --restrict-to-repositoryRecheck 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 workedClient and server Borg versions mismatch across major versionsEnsure both sides run the same Borg major version: borg --version on each. Upgrade the older one.
    Failed to create/acquire the lockA previous backup run crashed and left a stale lockborg break-lock :: on the client. Safe to run when no backup is active.
    Remote: borg: command not foundBorg not installed on the repository host, or not in the PATH that ssh non-interactive sessions seesudo apt install borgbackup on the repo host, then test with ssh borg@repo "borg --version".
    First archive takes many hoursInitial upload transfers all unique data, limited by bandwidthExpected — subsequent archives deduplicate. Consider seeding the repo over LAN/physical before moving the target to its final location.
    borg check reports corrupted chunksStorage corruption on the repository hostRestore 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 appearencryption_passcommand failing silentlysudo 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 createCache rebuild on a very large repoTemporarily 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 restoreFUSE mount over SSH on a high-latency linkUse borg extract instead of borg mount for full restores; mount is fine for browsing but slow for bulk copy.

    Viewing logs

    bash
    sudo journalctl -u borgmatic.service -n 100 --no-pager

    Follow live:

    bash
    sudo journalctl -u borgmatic.service -f

    For one-off debugging, run borgmatic with maximum verbosity:

    bash
    sudo borgmatic --verbosity 2 --syslog-verbosity 0

    FAQ

    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_error and after_backup hooks in config.yaml to 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.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket