Installing and Configuring BorgBackup
This guide covers the full BorgBackup workflow: installation, preparing SSH access, initializing a repository, creating backups, scheduling them with a bash script and cron, and restoring data. The primary scenario is unencrypted. Mounting archives and working with encryption are moved to the end as additional sections. See borgbackup.org for upstream documentation.
1. Installing borg
Debian / Ubuntu and derivatives:
sudo apt update sudo apt install -y borgbackup
Fedora / RHEL / CentOS / AlmaLinux / Rocky Linux:
sudo dnf install -y borgbackup
openSUSE:
sudo zypper install -y borgbackup
Arch Linux:
sudo pacman -S borgbackup
Verify the installation:
borg --version
2. Preparing SSH access
Create an SSH key:
ssh-keygen
Copy the key to the backup server:
ssh-copy-id backup-test-s3@backup-test-s3.backup.colocall.com
Verify that key-based login works:
ssh 'backup-test-s3@backup-test-s3.backup.colocall.com' 'echo "ok"'
If the command returns ok, the connection works.
3. Initializing the borg repository (no encryption)
borg init --encryption=none backup-test-s3@backup-test-s3.backup.colocall.com:/backup/borg-repo-1
In none mode the data is not encrypted and no passphrase is used — no borg command will ask for a password. This is the simplest option for a trusted environment.
If you need encryption and data integrity, see Appendix B. Encryption and passphrase at the end of this document.
4. Creating a backup
Create the first archive (call it Monday) from the ~/src and ~/Documents directories:
borg create --compression zstd \ backup-test-s3@backup-test-s3.backup.colocall.com:/backup/borg-repo-1::Monday \ ~/src ~/Documents
The next day, create a new archive called Tuesday:
borg create --stats --compression zstd \ backup-test-s3@backup-test-s3.backup.colocall.com:/backup/borg-repo-1::Tuesday \ ~/src ~/Documents
This backup is much faster and much smaller, because only new, never-before-seen data is stored (deduplication). The –stats option makes Borg print statistics about the newly created archive, such as the amount of unique data (not shared with other archives):
------------------------------------------------------------------------------
Archive name: Tuesday
Archive fingerprint: bd31004d58f51ea06ff735d2e5ac49376901b21d58035f8fb05dbf866566e3c2
Time (start): Tue, 2016-02-16 18:15:11
Time (end): Tue, 2016-02-16 18:15:11
Duration: 0.19 seconds
Number of files: 127
------------------------------------------------------------------------------
Original size Compressed size Deduplicated size
This archive: 4.16 MB 4.17 MB 26.78 kB
All archives: 8.33 MB 8.34 MB 4.19 MB
Unique chunks Total chunks
Chunk index: 132 261
------------------------------------------------------------------------------
List all archives in the repository:
borg list backup-test-s3@backup-test-s3.backup.colocall.com:/backup/borg-repo-1
Monday Mon, 2016-02-15 19:14:44 Tuesday Tue, 2016-02-16 19:15:11
In real use, replace ~/src ~/Documents with your actual data, e.g. /etc/httpd /var/data /home/user1 /home/user2.
5. Scheduling backups via a bash script and cron
Regular backups are implemented with a bash script run by cron. The script runs borg create, then borg prune (removes old archives per the retention policy) and borg compact (frees space). Since the repository is unencrypted, no passphrase is needed.
Prerequisites
- borg is installed (check the path with
which borg). - The remote repository is initialized (
borg init –encryption=none). - An SSH key for passwordless access, located at
/root/.ssh/id_rsa.
Step 1. The backup script
Create the file /usr/local/bin/borg-backup.sh:
#!/bin/sh # cron has a minimal PATH — set it explicitly so borg is found: export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" # So the repo does not need to be given on the command line: export BORG_REPO=ssh://backup-test-s3@backup-test-s3.backup.colocall.com/backup/borg-repo-1 # SSH key and host-key checking (the script runs as root via cron): export BORG_RSH="ssh -i /root/.ssh/id_rsa -o StrictHostKeyChecking=accept-new" # Unencrypted repository — no passphrase needed. # For an encrypted repository see Appendix B. # some helpers and error handling: info() { printf "\n%s %s\n\n" "$( date )" "$*" >&2; } trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM info "Starting backup" # Backup the most important directories into an archive named after the host: borg create \ --verbose \ --filter AME \ --list \ --stats \ --show-rc \ --compression zstd \ --exclude-caches \ --exclude 'home/*/.cache/*' \ --exclude 'var/tmp/*' \ \ ::'{hostname}-{now}' \ /etc \ /home \ /root \ /var backup_exit=$? info "Pruning repository" # Keep 7 daily, 4 weekly and 6 monthly archives of THIS machine. # The '{hostname}-*' match is critical: prune won't touch other machines' archives. borg prune \ --list \ --glob-archives '{hostname}-*' \ --show-rc \ --keep-daily 7 \ --keep-weekly 4 \ --keep-monthly 6 prune_exit=$? # Actually free repo disk space by compacting segments: info "Compacting repository" borg compact compact_exit=$? # use highest exit code as global exit code: global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit )) global_exit=$(( compact_exit > global_exit ? compact_exit : global_exit )) if [ ${global_exit} -eq 0 ]; then info "Backup, Prune, and Compact finished successfully" elif [ ${global_exit} -eq 1 ]; then info "Backup, Prune, and/or Compact finished with warnings" else info "Backup, Prune, and/or Compact finished with errors" fi exit ${global_exit}
Make the script executable and readable only by root:
sudo chown root:root /usr/local/bin/borg-backup.sh sudo chmod 700 /usr/local/bin/borg-backup.sh
Key points explained
- {hostname}-{now} — borg substitutes the host name and current time into the archive name itself (unlike systemd, no
%escaping is needed here). - –filter AME together with
–listlogs only Added (A), Modified (M), and Errored (E) files; without–listthe filter shows nothing. - –exclude-caches skips directories tagged with a
CACHEDIR.TAGfile. - borg prune with
–glob-archives '{hostname}-*' removes old archives of this machine only, per the 7 days / 4 weeks / 6 months policy. - borg compact actually frees space: in Borg 1.2+
pruneonly marks archives for deletion.
Step 2. Schedule with cron
Create the file /etc/cron.d/borg-backup (runs daily at 02:00 as root, with logging):
# min hour day month weekday user command 0 2 * * * root /usr/local/bin/borg-backup.sh >> /var/log/borg-backup.log 2>&1
Other schedule options
0 2 * * * # daily at 02:00 30 23 * * 1-5 # weekdays at 23:30 0 6,18 * * * # twice a day: 06:00 and 18:00 0 3 * * 1 # every Monday at 03:00 0 4 1 * * # on the 1st of the month at 04:00
Step 3. Verification
# Run the backup manually without waiting for the schedule (to verify) sudo /usr/local/bin/borg-backup.sh # Make sure the cron job is in place cat /etc/cron.d/borg-backup # View the log of the last run tail -n 50 /var/log/borg-backup.log
Security (SSH)
StrictHostKeyChecking=accept-new trusts only a new key on first connection. The value no disables checking entirely and opens the door to a MITM attack, so it is best avoided; ideally pre-populate ~/.ssh/known_hosts.
6. Restoring from a backup
First list the archives and choose the one you need:
borg list backup-test-s3@backup-test-s3.backup.colocall.com:/backup/borg-repo-1
Monday Mon, 2016-02-15 19:14:44 Tuesday Tue, 2016-02-16 19:15:11
List the contents of a specific archive:
borg list backup-test-s3@backup-test-s3.backup.colocall.com:/backup/borg-repo-1::Monday
drwxr-xr-x user group 0 Mon, 2016-02-15 18:22:30 home/user/Documents -rw-r--r-- user group 7961 Mon, 2016-02-15 18:22:30 home/user/Documents/Important.doc ...
Borg extracts files relative to the current directory, so move into a safe location to avoid overwriting existing data:
mkdir /tmp/borg-restore cd /tmp/borg-restore
Restore the entire Monday archive:
borg extract backup-test-s3@backup-test-s3.backup.colocall.com:/backup/borg-repo-1::Monday
To restore only a single file or directory, append its path inside the archive:
borg extract backup-test-s3@backup-test-s3.backup.colocall.com:/backup/borg-repo-1::Monday \ home/user/Documents/Important.doc
Appendix A. Mounting backups (additional method)
Instead of borg extract, archives can be mounted via FUSE and browsed like ordinary directories. This is convenient for inspecting and selectively copying individual files.
Installing FUSE
For .deb-based systems:
sudo apt-get install -y -q fuse libfuse2
For .rpm-based systems:
sudo dnf install -y fuse fuse-libs
Mount the entire repository (all archives as directories)
mkdir -p /mnt/borg-repo borg mount backup-test-s3@backup-test-s3.backup.colocall.com:/backup/borg-repo-1 /mnt/borg-repo ls -la /mnt/borg-repo/
total 4 drwxr-xr-x 1 root root 0 Jun 1 15:40 . drwxr-xr-x 5 root root 4096 Jun 1 15:40 .. drwxr-xr-x 1 root root 0 Jun 1 15:21 Monday
Mount a specific archive
mkdir -p /mnt/borg-archive # Review the list of archives borg list backup-test-s3@backup-test-s3.backup.colocall.com:/backup/borg-repo-1 borg mount backup-test-s3@backup-test-s3.backup.colocall.com:/backup/borg-repo-1::Monday /mnt/borg-archive ls -al /mnt/borg-archive
Unmounting
When you are done, unmount the backups:
umount /mnt/borg-archive umount /mnt/borg-repo
Appendix B. Encryption and passphrase (additional info)
If you need data confidentiality and integrity, initialize the repository in an encrypted mode. This is a separate, more involved scenario compared to the primary (none) one.
Initializing with encryption
repokey mode (the key is stored in the repository):
borg init --encryption=repokey backup-test-s3@backup-test-s3.backup.colocall.com:/backup/borg-repo
Borg will prompt you to enter a passphrase. Type it directly into the terminal — it is not echoed on screen.
Important: you will need BOTH the KEY AND the PASSPHRASE to access an encrypted repository.
The key storage location depends on the mode:
- repokey — the key is stored in the repository directory.
- keyfile — the key is stored in the user's home directory.
Backing up the key
Always export the key and store it in a safe place:
borg key export REPOSITORY encrypted-key-backup borg key export --paper REPOSITORY encrypted-key-backup.txt borg key export --qr-html REPOSITORY encrypted-key-backup.html
Keep the passphrase separately in a secure location (a secret manager, vault, etc.).
Passphrase in the backup script
For an encrypted repository, the script must pass the passphrase to borg. Do not put it directly in the script — move it into a protected file.
Create /etc/borg/borg.env:
BORG_PASSPHRASE=repository_passphrase
Restrict access:
sudo install -d -m 700 /etc/borg sudo chmod 600 /etc/borg/borg.env
At the top of /usr/local/bin/borg-backup.sh (right after the export lines), source this file:
# load the passphrase from the protected file . /etc/borg/borg.env export BORG_PASSPHRASE
The rest of the script stays unchanged.
Passphrase security
Storing the password in a separate file (permissions 600) is safer than an export BORG_PASSPHRASE='…' line directly in the script. An alternative is BORG_PASSCOMMAND, reading the password from a protected source.
Data consistency during backup
Borg reads each file in whatever state it is in when Borg gets to it, and does not guarantee internal consistency of active data. A file may change between the start of the backup and the moment Borg reaches it, or while it is being read. For a set of files that must be backed up in a consistent state, take precautions:
- do not run programs that change those files during the backup;
- snapshot files, filesystems, or volumes — e.g. with LVM or ZFS;
- dump databases or stop the database servers;
- shut down virtual machines before backing up their images;
- shut down containers before backing up their storage volumes.
For a typical home directory on a low-activity system, borg usually works fine without these precautions. For databases, VMs, and containers, use their specific techniques (dump the DB between transactions, mount the filesystem of a shut-down VM, docker save, etc.).