diff --git a/README.md b/README.md index 8ee0a38..22fdf52 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,40 @@ You can finetune the retention cycle of the restic backups using the `PRUNE_REST :warning: | SFTP restic backend is not directly supported. Please use RCLONE backend with SFTP support. ---|--- +##### `rclone` backup method +Rclone acts as the `tar` backup method but automatically moves the compressed files to a remote drive via [rclone](https://rclone.org/). + +There are a few special environment variables for the rclone method. + +- `RCLONE_REMOTE` is the name of the remote you've configured in your rclone.conf, see [remote setup](https://rclone.org/remote_setup/). +- `RCLONE_COMPRESS_METHOD`=gzip +- `DEST_DIR` acts as the directory on the remote. Files are not stored locally when using rclone. + +Other parameters such as `PRUNE_BACKUPS_DAYS`, `ZSTD_PARAMETERS`, and `BACKUP_NAME` are all used as well. + +**Note** that you will need to place your rclone config file in `/config/rclone/rclone.conf`. +This can be done by adding it through docker-compose, + +```yaml +- ./rclone.config:/config/rclone/rclone.conf:ro +``` +or by running the config wizard in a container and mounting the volume. +```shell +docker run -it --rm -v rclone-config:/config/rclone rclone/rclone config +``` + +then you must bind the volume **for the mc-backup process** +```yaml +volumes: + - rclone-config:/config/rclone +``` +**and the service** +```yaml +volumes: + rclone-config: + external: true +``` + ## Volumes - `/data` : diff --git a/backup-loop.sh b/backup-loop.sh index f2ca909..4d9c970 100755 --- a/backup-loop.sh +++ b/backup-loop.sh @@ -30,6 +30,8 @@ fi : "${XDG_CONFIG_HOME:=/config}" # for rclone's base config path : "${ONE_SHOT:=false}" : "${TZ:=Etc/UTC}" +: "${RCLONE_COMPRESS_METHOD:=gzip}" +: "${RCLONE_REMOTE:=}" export TZ export RCON_HOST @@ -296,6 +298,65 @@ restic() { call_if_function_exists "${@}" } +rclone() { + _find_old_backups() { + command rclone lsf --format "tp" "${RCLONE_REMOTE}:${DEST_DIR}" | grep ${BACKUP_NAME} | awk \ + -v DESTINATION="${DEST_DIR}" \ + -v PRUNE_DATE="$(date '+%Y-%m-%d %H:%M:%S' --date="${PRUNE_BACKUPS_DAYS} days ago")" \ + 'BEGIN { FS=";" } $1 < PRUNE_DATE { printf "%s/%s\n", DESTINATION, $2 }' + } + init() { + # Check if rclone is installed and configured correctly + mkdir -p "${DEST_DIR}" + case "${RCLONE_COMPRESS_METHOD}" in + gzip) + readonly tar_parameters=("--gzip") + readonly backup_extension="tgz" + ;; + + bzip2) + readonly tar_parameters=("--bzip2") + readonly backup_extension="bz2" + ;; + + zstd) + readonly tar_parameters=("--use-compress-program" "zstd ${ZSTD_PARAMETERS}") + readonly backup_extension="tar.zst" + ;; + + *) + log ERROR 'RCLONE_COMPRESS_METHOD is not valid!' + exit 1 + ;; + esac + } + backup() { + ts=$(date +"%Y%m%d-%H%M%S") + outFile="${BACKUP_NAME}-${ts}.${backup_extension}" + log INFO "Backing up content in ${SRC_DIR} to ${outFile}" + command tar "${excludes[@]}" "${tar_parameters[@]}" -cf "${outFile}" -C "${SRC_DIR}" . || exitCode=$? + if [ ${exitCode:-0} -eq 1 ]; then + log WARN "tar exited with code 1. Ignoring" + fi + if [ ${exitCode:-0} -gt 1 ]; then + log ERROR "tar exited with code ${exitCode}! Aborting" + exit 1 + fi + + command rclone copy "${outFile}" "${RCLONE_REMOTE}:${DEST_DIR}" + rm "${outFile}" + } + prune() { + if [ -n "$(_find_old_backups)" ]; then + log INFO "Pruning backup files older than ${PRUNE_BACKUPS_DAYS} days" + _find_old_backups | tee \ + >(awk '{ printf "Removing %s\n", $0 }' | log INFO) \ + >(while read -r path; do command rclone purge "${path}"; done) + fi + } + call_if_function_exists "${@}" +} + ########## ## main ## ##########