-
Notifications
You must be signed in to change notification settings - Fork 0
Backup with Borg
Borg is a software for backup creation under linux, see https://borgbackup.readthedocs.io. It uses deduplication in a similar way like git.
Separate bash scripts for each process as automatisation would make sense.
sudo pacman -S borg
See also: https://borgbackup.readthedocs.io/en/stable/installation.html.
mkdir
the directory (where the repo data should reside) first.
borg init --encryption=<mode> /path/to/backup/repo
# For remote repositories (via ssh)
borg init --encryption=<mode> user@hostname:/path/to/repo # See: https://borgbackup.readthedocs.io/en/stable/quickstart.html#remote-repositories
Initialises a backup repository with a repository key - it will be asked for a key - and a path.
Hash func | Auth-only args | Encryption & auth mode (passphrase only) | Encryption & auth mode (passphrase + keyfile) |
---|---|---|---|
SHA-256 | authenticated |
repokey |
keyfile |
BLAKE2b | authenticated-blake2 |
repokey-blake2 |
keyfile-blake2 |
- To automate backups with passphrase set the
BORG_PASSPHRASE
env variable - Or use
--encryption none
for not encryption - If no encryption is wanted but still to detect malicious tampering
- Use
--encryption authenticated
- To work with authenticated repos you will need the passphrase, but there is an emergency workaround:
BORG_WORKAROUNDS=authenticated_no_key
- Use
- Keyfiles will be stored in
.config/borg/keys
- Speed
- Generally
BLAKE2b
is faster thanSHA-256
(Intel/AMD 64-bit CPUs) -
authenticated-blake2
is faster thannone
&authenticated
-
--encryption
can benone
(no passphrase, no keyfile) -> howeverSHA-256
is used as chunk ID hash -> not the fastest option - See also here in the benchmarking section of the borg docs
- Generally
If you use keyfiles export them via: borg key export /path/to/borg/repo /export/location/borg-repo.keyfile
(you must state a file not a folder).
We create a backup of /home/$user
but exclude some directories:
borg create /path/to/repo::'{hostname}-{now}' \
/home/$user \
--exclude '/home/*/.cache/*' \
--exclude "/home/$user/.xsession-errors" \
--exclude-caches \
--compression lz4 \
--progress \
--one-file-system
To get a feeling about compression/decompression speed and ratio see here: https://catchchallenger.first-world.info/wiki/Quick_Benchmark:_Gzip_vs_Bzip2_vs_LZMA_vs_XZ_vs_LZ4_vs_LZO.
List all created backups inside the repository via:
borg list /path/to/borg/repo
Diff borg snapshots
Prints a diff of two snapshots. You might want a list
before to check the available names.
borg diff /path/to/borg/repo::snapshot_name1 snapshot_name2
Note: if there are equal there is no output.
For checking if your command backups the correct things (like considering excludes correctly) this is a very useful command.
$ borg info /path/to/repo/
Enter passphrase for key /home/<usr>/.config/borg/keys/test_2.2:
Repository ID: e6cf32e10e7a81e4839e676670d0a5a7326a33edb425b91ead4d3c4fe01038a8
Location: /path/to/repo/
Encrypted: Yes (key file BLAKE2b)
Key file: /home/<usr>/.config/borg/keys/test_2.2
Cache: /home/<usr>/.cache/borg/e6cf32e10e7a81e4839e676670d0a5a7326a33edb425b91ead4d3c4fe01038a8
Security dir: /home/<usr>/.config/borg/security/e6cf32e10e7a81e4839e676670d0a5a7326a33edb425b91ead4d3c4fe01038a8
------------------------------------------------------------------------------
Original size Compressed size Deduplicated size
All archives: 130.40 MB 65.63 MB 3.46 MB
Unique chunks Total chunks
Chunk index: 155 2664
In order to extract a specific backup (point in time) to the current folder you can use the following command:
borg extract /path/to/borg/repo::backupNAME --progress
Notes:
- The whole path will be created. E.g. when your backuped folder is
/mnt/test_1/
it will also create the foldersmnt
andtest_1
. - In case you are wondering that you do not need any keyfiles have a look in
~/.config/borg/keys/
. Duringborg init
the keyfiles were created at this location.
Borg allows you to directly mount a specific backup to be mounted. In order to work you have to install another dependency: pip3 install llfuse
.
After the installation we are ready to go.
Get the list of available backups:
borg list /path/to/borg/repo
Do the actual mount:
borg mount /path/to/borg/backup/repo::2019-08-05-00:16:15-usr /mnt/temp/
# ^^^^^^^^^^^^^^^^^^^^^^^^^^
# || ^^^^^^^^^
# Snapshot name (from `borg list`) ||
# Will be mounted here
If you get a /mnt/temp/: Mountpoint must be a writable directory
error you probably don't have the correct permissions (-> chmod
will help).
borg umount /mnt/temp/
For automation this handy scripts might be useful:
Copy this file to your prefered location like. For example:
cp borg-automation.sh /opt/borg-backup/borg-automation.sh
sudo chmod 770 /opt/borg-backup/borg-automation.sh
#!/bin/bash
##################################
# Borg backup automation script
##################################
# Note, this script does NOT:
# * mount any disk/partitions
# * initially create your Borg repository
##################################
# User Configuration
PATH_TO_BACKUP="/mnt/test_1" # This path will be backed-up
BORG_REPO="/mnt/test_2" # Path to Borg repository
BACKUP_REPO_DISK_UUID="1CD0AF0C0975E9BA" # UUID of disk containing the Borg repository
BORG_PASSPHRASE="test"
BORG_COMPRESSION="lz4" # Compression method used for backup
#BORG_COMPRESSION="lzma"
# See also: https://catchchallenger.first-world.info/wiki/Quick_Benchmark:_Gzip_vs_Bzip2_vs_LZMA_vs_XZ_vs_LZ4_vs_LZO
##################################
# Predefined Configuration
export BORG_RELOCATED_REPO_ACCESS_IS_OK=no
export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no
export BORG_PASSPHRASE="${BORG_PASSPHRASE}"
#export BORG_REPOSITORY="${BORG_REPO}"
TIMESTAMP="$(date +"%Y-%m-%d-%H:%M:%S")-$(hostname)"
BORG_PRUNE() {
borg prune \
-v \
--stats \
--list \
--keep-yearly=-1 \
--keep-monthly=25 \
--keep-weekly=53 \
--keep-daily=90 \
${BORG_REPO}
}
BORG_CREATE() {
borg create \
--stats \
--progress \
--one-file-system \
--compression ${BORG_COMPRESSION} \
--exclude-caches \
--exclude '/mnt/some/path/TEMP' \ # edit excludes
--exclude '/dev' \ # The following are excludes for a complete system backup
--exclude '/home/*/.cache' \
--exclude '/root' \
--exclude '/home/<user>/share' \ # Add username!!
--exclude '/home/*/.config/borg/security' \
--exclude '/lost+found' \
--exclude '/tmp' \
--exclude '/mnt' \
--exclude '/proc' \
--exclude '/run' \
--exclude '/snap' \
--exclude '/sys' \
--exclude '/var/cache' \
--exclude '/var/lib/container' \
--exclude '/var/lib/lxcfs' \
--exclude '/var/lib/ooni' \
--exclude '/var/lib/snapd' \
--exclude '/var/lib/ubuntu-advantage' \
--exclude '/var/run' \
--exclude '/var/lock' \
--exclude '/var/spool' \
--exclude '/var/log' \
--exclude '/var/tmp' \
--exclude '/var/snap' \
--exclude '/etc/pacman.d/' \
--exclude '/etc/.pwd.lock' \
--exclude '/usr/bin' \
--exclude '/usr/include' \
--exclude '/usr/lib' \
--exclude '/usr/lib32' \
--exclude '/usr/lib64' \
--exclude '/usr/share' \
--exclude '/usr/src' \
--exclude '/usr/sbin' \
${BORG_REPO}::${TIMESTAMP} \
${PATH_TO_BACKUP}
}
# Reganding excludes for system backup see: https://unix.stackexchange.com/questions/1067/what-directories-do-i-need-to-back-up
####################################################################
SEPARATOR="-------------------------------"
SEPARATOR_LONG="---------------------------------------------------------------------------------------------"
HEADING="#################################
# BORG BACKUP AUTOMATION SCRIPT #
#################################"
echo "${HEADING}"
# Check if correct drive is mounted and avoid bloating drives by accident
foundUUID="$(lsblk -o uuid --noheading | grep ${BACKUP_REPO_DISK_UUID})"
lsblkOutLong="$(lsblk -o name,mountpoint,label,fsused,fssize,size,fstype,uuid)"
foundUUID_LONG="$(grep ${BACKUP_REPO_DISK_UUID} <<< ${lsblkOutLong})"
echo ""
if [[ -z "${foundUUID}" ]]; then
echo "No backup disk found; exiting ..."
printf "Mounted disks were:\n\n${lsblkOutLong}\n"
exit 0
else
echo "${SEPARATOR_LONG}"
printf "Found backup disk:\n\n"
head -n 1 <<< ${lsblkOutLong}
echo "${foundUUID_LONG}"
echo "${SEPARATOR_LONG}"
fi
echo ""
# Check if directory to backup is not empty (then it is possibly not mounted)
if [ -n "$(ls -A ${PATH_TO_BACKUP} 2>/dev/null)" ]
then
echo "All clear; directory to backup is not empty."
else
echo "Directory to backup is empty. Exiting..."
exit 0
fi
# Print version for logging
echo "Borg version: $(borg --version)"
echo "Starting pruning. This may take some moments ..."
printf "\tExecution string: \`$(declare -f BORG_PRUNE)\`\n\n"
BORG_PRUNE
printf "Done with pruning.\n\n"
echo "Starting backup for \`${TIMESTAMP}\`. This may take some moments ..."
printf "\tExecution string: \`$(declare -f BORG_CREATE)\`\n\n"
BORG_CREATE
echo "Done with backup \`${TIMESTAMP}\`."
# Reset password env var
export BORG_PASSPHRASE=""
# Just be sure that something was still cached but not written to disk
sync
#!/bin/bash
##################################
# Borg backup automation script
##################################
# Note, this script does NOT:
# * mount any disk/partitions
# * initially create your Borg repository
## INIT
# Command used
# borg init --encryption=none /mnt/h/my_backup
echo "Make sure the backup is mounted as /mnt/h and the disk to backup is mounted as /mnt/x"
read -r -p "Are you sure? [y/N] " response
if ! [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
echo "Exiting..."
exit
fi
##################################
# User Configuration
PATH_TO_BACKUP="/mnt/x" # This path will be backed-up
BORG_REPO="/mnt/h/my_backup" # Path to Borg repository
BORG_COMPRESSION="lz4" # Compression method used for backup
#BORG_COMPRESSION="lzma"
# See also: https://catchchallenger.first-world.info/wiki/Quick_Benchmark:_Gzip_vs_Bzip2_vs_LZMA_vs_XZ_vs_LZ4_vs_LZO
##################################
# Predefined Configuration
export BORG_RELOCATED_REPO_ACCESS_IS_OK=no
export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no
TIMESTAMP="$(date +"%Y-%m-%d-%H:%M:%S")-$(hostname)"
BORG_PRUNE() {
borg prune \
-v \
--stats \
--list \
--keep-yearly=-1 \
--keep-monthly=25 \
--keep-weekly=53 \
--keep-daily=90 \
${BORG_REPO}
}
BORG_CREATE() {
borg create \
--stats \
--progress \
--one-file-system \
--compression ${BORG_COMPRESSION} \
--exclude-caches \
--exclude '/mnt/x/paths/to/exclude/' \
${BORG_REPO}::${TIMESTAMP} \
${PATH_TO_BACKUP}
}
####################################################################
SEPARATOR="-------------------------------"
SEPARATOR_LONG="---------------------------------------------------------------------------------------------"
HEADING="#################################
# BORG BACKUP AUTOMATION SCRIPT #
#################################"
echo "${HEADING}"
# Check if directory to backup is not empty (then it is possibly not mounted)
if [ -n "$(ls -A ${PATH_TO_BACKUP} 2>/dev/null)" ]
then
echo "All clear; directory to backup is not empty."
else
echo "Directory to backup is empty. Exiting..."
exit 0
fi
# Print version for logging
echo "Borg version: $(borg --version)"
echo "Starting pruning. This may take some moments ..."
printf "\tExecution string: \`$(declare -f BORG_PRUNE)\`\n\n"
BORG_PRUNE
printf "Done with pruning.\n\n"
echo "Starting backup for \`${TIMESTAMP}\`. This may take some moments ..."
printf "\tExecution string: \`$(declare -f BORG_CREATE)\`\n\n"
BORG_CREATE
echo "Done with backup \`${TIMESTAMP}\`."
# Just be sure that something was still cached but not written to disk
sync
#/etc/systemd/system/borg-backup.service
[Unit]
Description=Borg Backup
[Service]
# To avoid permission errors with the backup use the current user/group
User=yourUser
Group=users
Type=simple
# We do not want to interrupt other important tasks so we set the scheduling parameters accordingly
Nice=19
IOSchedulingClass=2
IOSchedulingPriority=7
# In case something went wrong during the last backup try to unlock the repository
ExecStartPre=/usr/bin/borg break-lock /mnt/test_2
ExecStart=/opt/borg-backup/borg-automation.sh
It suffice that .timer
and .service
components have the same name.
#/etc/systemd/system/borg-backup.timer
[Unit]
Description=Borg Backup Timer
[Timer]
WakeSystem=false
# Start every day at 04h
OnCalendar=*-*-* 04:00:00
[Install]
WantedBy=timers.target
Regarding OnCalendar
syntax see here.
Then refresh and enable:
systemctl enable borg-backup.timer
systemctl start borg-backup.timer
systemctl daemon-reload
# Check timer
systemctl list-timers --all
# Check service execution (when timer was triggered at `OnCalendar`-time)
systemctl status borg-backup.service
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License *.
Code (snippets) are licensed under a MIT License *.
* Unless stated otherwise