Last updated Apr 4, 2021

How to configure Borg client on macOS using command-line

Requirements

Caveats

  • When copy/pasting commands that start with $, strip out $ as this character is not part of the command
  • When copy/pasting commands that start with cat << "EOF", select all lines at once (from cat << "EOF" to EOF inclusively) as they are part of the same (single) command

Setup guide

Heads-up: steps 1 to 4 are only required if using BorgBase or rsync.net (don’t forget to enable 2FA).

Step 1: create borg SSH key pair (if using BorgBase or rsync.net)

When asked for file in which to save key, enter borg.

When asked for passphrase, use output from openssl rand -base64 24 (and store passphrase in password manager).

Use borg.pub public key when configuring Borg server.

$ mkdir -p ~/.ssh

$ cd ~/.ssh

$ ssh-keygen -t rsa -C "borg"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/sunknudsen/.ssh/id_rsa): borg
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in borg.
Your public key has been saved in borg.pub.
The key fingerprint is:
SHA256:9DzU/jDPyR/vGe8k2Yn1p31wF8UxLzCmYEj//D6+oYk borg
The key's randomart image is:
+---[RSA 3072]----+
|     ...o   +  +.|
|      .o . + o  =|
|        o o . . o|
|       . * .   o |
|        S * +  ..|
|           o B++=|
|            o.O**|
|         . +.. *O|
|        E o.+o.+O|
+----[SHA256]-----+

$ cat borg.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCwCdu7RCOmISZQ5cr43lDRPrFoxCXcVfCREYsdTIBEoQrIwyg1jZyzQMf9kORIGcNe5+olIj1aK9qVg0hCEeDSJosSsMP5o8tQJzNu5aYCtnADlZ+AuCgp5CpL1vECMaQsfQV9nju3ScE/+0C/MSYVvDx5sbRvi1XuutBbCAZtlUa7Rn7S8/X08XLFasM7KhFz7AH2Hvvi1i3Cg1WqkRKzpXE/uxntZ/qZxBdpa2WEN/phD4LgmmCbzKJYflhJNKJnYQZxGveGsdexdrDpEbajVECBw/0ntS5/YYaLxzqCrNGyCRdAajIccuOLQjRGzr9U5mdzVpHhkCLjbIDQ1JHxtb9nHxNgvGep7z0UCqawdcJN2nEr1D7Khu7Mh8mryR7iBxqEdPfdARuQn3kMFH+YA5NASTus9p/MR1cavJmBq3u88oNje8q+szkBsQDb1h0n0eAzjjDXRSxgm8bdtpi07TjTNCc+AmhYiym+MYXmbxqMO6pnjjE1I+ht3a8zUU0= borg

Step 2: create borg-append-only SSH key pair (if using BorgBase or rsync.net)

When asked for file in which to save key, enter borg-append-only.

When asked for passphrase, leave field empty for no passphrase (this public key will be used for append-only operations).

Use borg-append-only.pub public key when configuring Borg server.

$ ssh-keygen -t rsa -C "borg-append-only"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/sunknudsen/.ssh/id_rsa): borg-append-only
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in borg-append-only.
Your public key has been saved in borg-append-only.pub.
The key fingerprint is:
SHA256:Se6MQbWpFg0lWI2+fJ1IVPtUCs/ZRYrgtpz4F3hi2ow borg-append-only
The key's randomart image is:
+---[RSA 3072]----+
|     o++o.+   ..o|
|    . .=o+ * * o |
|     .o.= + B o  |
|     ..=.= *     |
|     .+oSoB.+    |
|     .o=oBoo .   |
|      ..E + .    |
|           .     |
|                 |
+----[SHA256]-----+

$ cat borg-append-only.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC2cmGUEKwopEN0vpHl2yNoV/wvm21D1hOP/8V886iCawgYpP5SUNpuVTDEgZEFJSvTMtfPaBicln0ULx8bp5NAiOQ8uPIvJD3xaacwISwvCVSYXY8jnQG3eRuhbKCU0aVFLONjnAvo288+NWbVcLw8Y166MPyk+tVz76plmv0LGefrZ0yPG99MngR3E5BLQk1EWQoH1kWGGHNFecFtMLq3usX23Ee4e605gfkWWoj7xSgpujfCHi/re6u7B25cn5t2eR7Ee0qRe/O2Sid2yIma7zK2l9NA0+k7pGngyXUTnGx9bI4+xM5qY0ZJcOQk03UJh52Gx8zXFASOxdGO71FiHvYKz60yyd5dUetPcBOYUygdejdBeBS36bh6SisXE/iI6aOfB/ViZd2ZNne1Fb7ijakyNsDCVEAWkMGJxnN8ZCapGsfG9YhKk/fU92Yxjos+AB1IC3M9Qjq5p8fZGsKdRtzJ3zxtTyk5dQEziAbmBVIJYyFohx/aCUB+MVF9xaM= borg-append-only

Step 3: configure SSH keys and create repo (if using BorgBase)

Configure SSH keys

Go to SSH Keys and add borg.pub and borg-append-only.pub keys.

Create repo

Go to Repositories and add repository.

Step 4: generate and upload authorized_keys file (if using rsync.net)

Set temporary environment variables

BORG_USERNAME="18434"
BORG_STORAGE_QUOTA="500G"
BORG_HOSTNAME="ch-s011.rsync.net"

Generate authorized_keys file

cat << EOF > ~/Desktop/authorized_keys
command="borg1 serve --restrict-to-repository /data1/home/$BORG_USERNAME/backup --storage-quota $BORG_STORAGE_QUOTA",restrict $(cat ~/.ssh/borg.pub)
command="borg1 serve --append-only --restrict-to-repository /data1/home/$BORG_USERNAME/backup --storage-quota $BORG_STORAGE_QUOTA",restrict $(cat ~/.ssh/borg-append-only.pub)
EOF

Upload authorized_keys file

scp ~/Desktop/authorized_keys $BORG_USERNAME@$BORG_HOSTNAME:.ssh/authorized_keys

Step 5: install Homebrew

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

Step 6: disable Homebrew analytics

brew analytics off

Step 7: install FUSE for macOS, Borg and Borg Wrapper

Heads-up: when installing Borg using brew install borgbackup, one can no longer run brew mount (see issue) so I created a tap that includes a patched version of borgbackup called borgbackup-fuse.

Heads-up: if brew install --cask macfuse fails, try brew cask install macfuse (see issue).

brew install --cask macfuse
brew install borgbackup/tap/borgbackup-fuse
brew install --cask sunknudsen/tap/borg-wrapper

Step 8: configure Borg

Generate Borg passphrase using openssl and add passphrase to “Keychain Access”

security add-generic-password -D secret -U -a $USER -s borg-passphrase -w $(openssl rand -base64 24)

Initialize Borg repo

Replace borg@185.112.147.115:backup with self-hosted or cloud-based repo.

$ export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"

$ export BORG_RSH="ssh -i ~/.ssh/borg"

$ borg init --encryption=keyfile-blake2 "borg@185.112.147.115:backup"
Enter passphrase for key '/Users/sunknudsen/.ssh/borg':

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 ssh://borg@185.112.147.115/./backup

See https://borgbackup.readthedocs.io/en/stable/changes.html#pre-1-0-9-manifest-spoofing-vulnerability for details about the security implications.

IMPORTANT: you will need both KEY AND PASSPHRASE to access this repo!
Use "borg key export" to export the key, optionally in printable format.
Write down the passphrase. Store both at safe place(s).

Back up ~/.config/borg and ~/Library/Keychains folders (learn how here)

Heads-up: both key (stored in ~/.config/borg) and passphrase (stored in ~/Library/Keychains) are required to decrypt backup.

Step 9: set temporary environment variables

Replace borg@185.112.147.115:backup with self-hosted or cloud-based repo and set backup name.

BORG_REPO="borg@185.112.147.115:backup"
BACKUP_NAME="$USER-macbook-pro"

Step 10: create /usr/local/bin/borg-backup.sh script

cat << EOF > /usr/local/bin/borg-backup.sh
#! /bin/sh

set -e

repo="$BORG_REPO"
prefix="$BACKUP_NAME-"

export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"

borg create \\
  --filter "AME" \\
  --list \\
  --stats \\
  --verbose \\
  "\$repo::\$prefix{now:%F-%H%M%S}" \\
  "/Users/$USER/.ssh" \\
  "/Users/$USER/Library/Keychains"

printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-backup.sh

Step 11: edit /usr/local/bin/borg-backup.sh script

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

Step 12: create /usr/local/bin/borg-list.sh script

cat << EOF > /usr/local/bin/borg-list.sh
#! /bin/sh

set -e

prefix="$BACKUP_NAME-"
repo="$BORG_REPO"

export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"

borg list --prefix "\$prefix" "\$repo"

printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-list.sh

Step 13: create /usr/local/bin/borg-check.sh script

cat << EOF > /usr/local/bin/borg-check.sh
#! /bin/sh

set -e

prefix="$BACKUP_NAME-"
repo="$BORG_REPO"

export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"

borg check --prefix "\$prefix" "\$repo"

printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-check.sh

Step 14: create /usr/local/bin/borg-restore.sh script

cat << EOF > /usr/local/bin/borg-restore.sh
#! /bin/sh

set -e

function umount()
{
  if [ -d "\$mount_point" ]; then
    borg umount \$mount_point
  fi
}

trap umount ERR INT

mount_point="\${TMPDIR}borg"
prefix="$BACKUP_NAME-"
repo="$BORG_REPO"

mkdir -p \$mount_point

export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"

borg mount --prefix "\$prefix" "\$repo" "\$mount_point"

open \$mount_point

printf "Restore data and press enter"

read -r answer

umount

printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-restore.sh

Step 15: create /usr/local/bin/borg-prune.sh script

cat << EOF > /usr/local/bin/borg-prune.sh
#! /bin/sh

set -e

prefix="$BACKUP_NAME-"
repo="$BORG_REPO"

export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg"

borg prune --keep-hourly 24 --keep-daily 31 --keep-weekly 52 --keep-monthly -1 --list --prefix "\$prefix" --stats "\$repo"

printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-prune.sh

Step 16: create /usr/local/var/log folder

mkdir -p /usr/local/var/log

Step 17: run “Borg Wrapper”

open /Applications/Borg\ Wrapper.app

Heads-up: given “Borg Wrapper” is developed outside the Apple Developer Program, macOS prevents opening the app without explicit user consent (granted by clicking “Open Anyway” in “System Preferences” / “Privacy & Security”).

Backup completed

👍

Step 18: schedule backup every hour using launchd

mkdir -p ~/Library/LaunchAgents
cat << "EOF" > ~/Library/LaunchAgents/local.borg-wrapper.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>Borg Wrapper.app</string>

    <key>ProgramArguments</key>
    <array>
      <string>open</string>
      <string>/Applications/Borg Wrapper.app</string>
    </array>

    <key>RunAtLoad</key>
    <false/>

    <key>StartCalendarInterval</key>
    <dict>
      <key>Minute</key>
      <integer>0</integer>
    </dict>
  </dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/local.borg-wrapper.plist

👍


Usage guide

Backup

$ borg-backup.sh
Creating archive at "borg@185.112.147.115:backup::sunknudsen-macbook-pro-{now:%F-%H%M%S}"
A /Users/sunknudsen/Library/Keychains/4FD89B1C-70AF-58EC-8026-35E97A08F9FE/keychain-2.db-wal
Remote: Storage quota: 314.36 kB out of 10.00 GB used.
Remote: Storage quota: 318.04 kB out of 10.00 GB used.
------------------------------------------------------------------------------
Archive name: sunknudsen-macbook-pro-2020-12-02-081439
Archive fingerprint: 781c5ca9dac166264250bdbe2c87aa1f9fb5f817cafd66d1e720dfdaa443f625
Time (start): Wed, 2020-12-02 08:14:41
Time (end):   Wed, 2020-12-02 08:14:42
Duration: 0.29 seconds
Number of files: 28
Utilization of max. archive size: 0%
------------------------------------------------------------------------------
                       Original size      Compressed size    Deduplicated size
This archive:                6.81 MB            312.77 kB              3.54 kB
All archives:               13.62 MB            625.53 kB            316.18 kB

                       Unique chunks         Total chunks
Chunk index:                      29                   56
------------------------------------------------------------------------------
Done

Done

👍

List

$ borg-list.sh
sunknudsen-macbook-pro-2020-12-02-081338 Wed, 2020-12-02 08:13:41 [c01b3400ec076adb993a2c268cd48810da9acc122614cdc4c87e00075464c1ee]
sunknudsen-macbook-pro-2020-12-02-081439 Wed, 2020-12-02 08:14:41 [781c5ca9dac166264250bdbe2c87aa1f9fb5f817cafd66d1e720dfdaa443f625]
Done

Done

👍

Check

$ borg-check.sh
Done

Done

👍

Restore

$ borg-restore.sh
mount_macfuse: the file system is not available (1)
umount: /var/folders/dl/mbmsd2m51nb8dvhmtz114j8w0000gn/T/borg: not currently mounted

Heads-up: given “FUSE for macOS” is a third-party extension, macOS prevents using the extension without explicit user consent (granted by clicking “Allow” in “System Preferences” / “Privacy & Security”).

$ borg-restore.sh
Restore data and press enter
Done

Done

👍

Prune

$ borg-prune.sh
Enter passphrase for key '/Users/sunknudsen/.ssh/borg':
Keeping archive: sunknudsen-macbook-pro-2020-12-02-081439 Wed, 2020-12-02 08:14:41 [781c5ca9dac166264250bdbe2c87aa1f9fb5f817cafd66d1e720dfdaa443f625]
Pruning archive: sunknudsen-macbook-pro-2020-12-02-081338 Wed, 2020-12-02 08:13:41 [c01b3400ec076adb993a2c268cd48810da9acc122614cdc4c87e00075464c1ee] (1/1)
------------------------------------------------------------------------------
                       Original size      Compressed size    Deduplicated size
Deleted data:               -6.81 MB           -312.76 kB             -3.52 kB
All archives:                6.81 MB            312.77 kB            312.65 kB

                       Unique chunks         Total chunks
Chunk index:                      27                   28
------------------------------------------------------------------------------
Done

Done

👍

Contributors:Sun KnudsenSun Knudsen
Reviewers:Be the first

Wish to contribute or need help? Read the docs.
This website is not tracking you. PGP public key fingerprint: C4FB DDC1 6A26 2672 920D  0A0F C132 3A37 7DE1 4C8B