BorgBackup is a secure backup solution which is also easy to use. It provides compression, encryption, deduplication and authentication.
Getting started
I’ve created a Dockerfile based on Alpine Linux which is also available on DockerHub. It gets built weekly to always stay up to date.
This Makefile
can be used to quickly get started using a containerized version of Borg:
SHELL := /bin/bash
VERSION ?= latest
# The directory of this file
DIR := $(shell echo $(shell cd "$(shell dirname "${BASH_SOURCE[0]}" )" && pwd ))
IMAGE_NAME ?= ps1337/borg-docker
CONTAINER_NAME ?= borg
# This will output the help for each task
# thanks to https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
.PHONY: help
help: ## This help
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
.DEFAULT_GOAL := help
# Build the container
build: ## Build the container
docker build --rm -t $(IMAGE_NAME) .
build-nc: ## Build the container without caching
docker build --rm --no-cache -t $(IMAGE_NAME) .
run: ## Run container
sudo docker run \
-d \
--name $(CONTAINER_NAME) \
-v $(DIR)/data:/var/backups/borg \
-v $(DIR)/authorized_keys:/home/borg/.ssh/authorized_keys \
-p 22:22 \
$(IMAGE_NAME):$(VERSION)
stop: ## Stop a running container
docker stop $(CONTAINER_NAME)
remove: ## Remove a (running) container
docker rm -f $(CONTAINER_NAME)
remove-image-force: ## Remove the latest image (forced)
docker rmi -f $(IMAGE_NAME):$(VERSION)
This docker run
call mounts two things:
./dir
to persistently store the backup data on the container host./authorized_keys
to perform authentication with the OpenSSH server of borg. You have to provide this file to get started.
Creating repositories and backups
To create a borg backup repository using a specified SSH private key, use
BORG_RSH='ssh -i </path/to/private/key>' borg init -e repokey <ssh://borg@yourDomain:exposedPort/var/backups/borg/repoName>
For each backup repository, you can create a borg backup script like this:
#!/bin/sh
export BORG_RSH='<ssh -i /path/to/private/key>'
# Setting this, so the repo does not need to be given on the commandline:
export BORG_REPO=<ssh://borg@yourDomain:exposedPort/var/backups/borg/repoName>
# Setting this, so you won't be asked for your repository passphrase:
export BORG_PASSPHRASE=$(<Password manager call to get the secret key>)
HOSTNAME=`hostname`
DATE=`date +%Y-%m-%d-%s `
# 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 machine this script is currently running on:
borg create \
--verbose \
--filter AME \
--list \
--progress \
--stats \
--show-rc \
--compression zlib \
--exclude-caches \
--exclude '/home/*/.cache/*' \
::$HOSTNAME-$DATE \
/home
backup_exit=$?
if [ ${backup_exit} -eq 0 ];
then
info "Pruning repository"
# Use the `prune` subcommand to maintain 7 daily, 4 weekly and 6 monthly
# archives of THIS machine. The '{hostname}-' prefix is very important to
# limit prune's operation to this machine's archives and not apply to
# other machines' archives also:
borg prune \
--list \
--prefix $HOSTNAME- \
--show-rc \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
prune_exit=$?
# use highest exit code as global exit code
global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit ))
if [ ${global_exit} -eq 1 ];
then
info "Backup and/or Prune finished with a warning"
fi
if [ ${global_exit} -gt 1 ];
then
info "Backup and/or Prune finished with an error"
fi
exit ${global_exit}
fi
exit ${backup_exit}
This script can be called daily or using a cronjob.
After creating a few backups, the results can look like this:
Original size Compressed size Deduplicated size
This archive: 75.24 GB 61.03 GB 53.69 GB
All archives: 100.14 GB 84.17 GB 53.69 GB
Note the deduplicated size vs. original size, showing the amount of space that’s possible to save using borg.