I’ve been running duplicacy in docker on my home server for quite a while now, and happy about it. Here’s how I do it.
run.sh
is scheduled using cron (once a day for me; choose whatever frequency fits you mood):
#!/bin/bash
set -e
if [[ $EUID -ne 0 ]]; then
>&2 echo "This script must be run as root."
exit 17
fi
# chdir to folder that contains this script
cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1
LOG="$(pwd)/run.log"
echo "[$(date)] Starting backups using duplicacy ..." >> "${LOG}"
echo "[$(date)] Building duplicacy docker ..." >> "${LOG}"
(cd docker-build/ ; sudo docker build -t duplicacy . >/dev/null)
echo "[$(date)] Done." >> "${LOG}"
for d in _vol/backups/*; do
echo "[$(date)] Starting backup of $d ..." >> "${LOG}"
docker run --rm \
--name duplicacy-backup-to-gdrive \
--hostname duplicacy \
--cpu-shares 512 \
-v "$(pwd)/_vol/scripts":/scripts \
-v "$(pwd)/$d:/backups" \
-v "$(pwd)/_vol/gcd-token.json":/backups/.duplicacy/gcd-token.json \
-t duplicacy \
/scripts/duplicacy-backup.sh "/backups"
echo "[$(date)] Done." >> "${LOG}"
done
That script lists folders (symlinks to folders really) in _vol/backups/
, and run duplicacy backup
on each of them, one by one.
Examples of what I have in my _vol/backups/
folder:
etc -> /etc
'Home Movies' -> '/mnt/samba/Videos/Home Movies'
'Other backups' -> '/mnt/samba/Backups/Other backups'
persistent-app-data -> /mnt/hdd5/persistent-app-data
Photos -> /mnt/samba/Photos
For duplicacy to be able to backup each of those, they each need to have their own .duplicacy/preferences|filters
configuration (with a different snapshot id
). But I am backing them all up to the same storage (Google Drive), so I’ve put the required gcd-token.json
in my _vol
folder, and I just mount it into /backups/.duplicacy/gcd-token.json
(which is the reference I need to have in my preferences
files).
Side-note: By specifying --name=...
when running my docker container, I ensure no two backups will be executed simultaneously, because container names needs to be unique.
/scripts/duplicacy-backup.sh
is just a helper script I use to run duplicacy backup
; I prefer to mount it when I run my container, versus including it in the container image, because it makes it easier to modify it and just re-run the container, without having to re-build the image. (It’s for development mostly…)
Keeping the relevant parts, here’s my scripts/duplicacy-backup.sh
:
#!/bin/bash
export REPO="/backups"
export BACKUP_OPT="-stats -threads 6"
export CHECK_OPT="-stats"
export PRUNE_OPT="-all -threads 6 -keep 0:3650 365:365 30:30 -keep 7:1" # -keep <n:m> [+] keep 1 revision every n days for revisions older than m days
export EXEC="/usr/local/bin/duplicacy"
export EXEC_OPT="-log"
export LOG_DIR="${REPO}/.duplicacy/logs"
## End of config
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root" 1>&2
exit 1
fi
cd "${REPO}" || exit
mkdir -p "${LOG_DIR}"
LOG="${LOG_DIR}/backup-log-$(date +'%Y%m%d-%H%M%S')"
COMMAND="${EXEC} ${EXEC_OPT} backup ${BACKUP_OPT}"
echo "Command: ${COMMAND}" >> "${LOG}"
# shellcheck disable=SC2086
nice ${COMMAND} >> "${LOG}" 2>&1
LOG_LINES=$(grep -v " INFO " "${LOG}" | grep -v "^Command:")
if [ "${LOG_LINES}" != "" ]; then
echo "${LOG_LINES}" | mail -s "duplicacy backup warnings/errors" guillaume@me.com
fi
# Once a month, do a check
if [ "$(date +'%d')" = "01" ]; then
touch .duplicacy/ran_check
LOG="${LOG_DIR}/check-log-$(date +'%Y%m%d-%H%M%S')"
COMMAND="${EXEC} ${EXEC_OPT} check ${CHECK_OPT}"
echo "Command: ${COMMAND}" >> "${LOG}"
# shellcheck disable=SC2086
nice ${COMMAND} >> "${LOG}" 2>&1
LOG_LINES=$(grep -v " INFO " "${LOG}" | grep -v "^Command:")
if [ "${LOG_LINES}" != "" ]; then
echo "${LOG_LINES}" | mail -s "duplicacy check warnings/errors" guillaume@me.com
fi
elif [ "$(date +'%d')" = "02" ]; then
rm -f .duplicacy/ran_check
fi
LOG="${LOG_DIR}/prune-log-$(date +'%Y%m%d-%H%M%S')"
COMMAND="${EXEC} ${EXEC_OPT} prune ${PRUNE_OPT}"
echo "Command: ${COMMAND}" >> "${LOG}"
# shellcheck disable=SC2086
nice ${COMMAND} >> "${LOG}" 2>&1
LOG_LINES=$(grep -v " INFO " "${LOG}" | grep -v "^Command:")
if [ "${LOG_LINES}" != "" ]; then
echo "${LOG_LINES}" | mail -s "duplicacy prune warnings/errors" guillaume@me.com
fi
# Delete empty log files
find "${LOG_DIR}" -type f -size 0 -delete
# Delete old log files
find "${LOG_DIR}" -type f -mtime +30 -delete
Finally, here’s my docker-build/Dockerfile
:
FROM alpine:3.10
RUN apk add --no-cache curl bash ssmtp mailx php-cli
ARG DUPLICACY_VERSION=2.6.2
RUN curl -sLo /usr/local/bin/duplicacy "https://github.com/gilbertchen/duplicacy/releases/download/v$DUPLICACY_VERSION/duplicacy_linux_x64_$DUPLICACY_VERSION" \
&& chmod +x /usr/local/bin/duplicacy
# SSMTP (to be able to send emails)
COPY config/ssmtp.conf /etc/ssmtp/ssmtp.conf
RUN echo "hostname=`hostname`.home.me.com" >> /etc/ssmtp/ssmtp.conf
RUN apk add --no-cache tzdata && cp /usr/share/zoneinfo/America/Montreal /etc/localtime && echo "America/Montreal" > /etc/timezone && apk del tzdata
CMD ["bash"]
LABEL description="duplicacy" \
duplicacy="duplicacy v$DUPLICACY_VERSION" \
maintainer="Guillaume Boudreau <guillaume@me.com>"