diff --git a/README.md b/README.md index 1e6ac36..ed479ef 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ It handles __recurring or one-off backups of Docker volumes__ to a __local direc - [Manually triggering a backup](#manually-triggering-a-backup) - [Update deprecated email configuration](#update-deprecated-email-configuration) - [Using a custom Docker host](#using-a-custom-docker-host) + - [Run multiple backup schedules in the same container](#run-multiple-backup-schedules-in-the-same-container) - [Recipes](#recipes) - [Backing up to AWS S3](#backing-up-to-aws-s3) - [Backing up to Filebase](#backing-up-to-filebase) @@ -653,6 +654,32 @@ DOCKER_HOST=tcp://docker_socket_proxy:2375 In case you are using a socket proxy, it must support `GET` and `POST` requests to the `/containers` endpoint. If you are using Docker Swarm, it must also support the `/services` endpoint. If you are using pre/post backup commands, it must also support the `/exec` endpoint. +### Run multiple backup schedules in the same container + +Multiple backup schedules with different configuration can be configured by mounting an arbitrary number of configuration files (using the `.env` format) into `/etc/dockervolumebackup/conf.d`: + +``` +version: '3' + +services: + # ... define other services using the `data` volume here + backup: + image: offen/docker-volume-backup:latest + volumes: + - data:/backup/my-app-backup:ro + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./configuration:/etc/dockervolumebackup/conf.d + +volumes: + data: +``` + +A separate cronjob will be created for each config file. +If a configuration value is set both in the global environment as well as in the config file, the config file will take precedence. +The `backup` command expects to run on an exclusive lock, so it is your responsibility to make sure the invocations do not overlap. +In case you need your schedules to overlap, you need to create a dedicated container for each schedule instead. +When changing the configuration, you currently need to manually restart the container for the changes to take effect. + ## Recipes This section lists configuration for some real-world use cases that you can mix and match according to your needs. diff --git a/entrypoint.sh b/entrypoint.sh index e579435..3438f20 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -5,10 +5,21 @@ set -e -BACKUP_CRON_EXPRESSION="${BACKUP_CRON_EXPRESSION:-@daily}" +if [ ! -d "/etc/dockervolumebackup/conf.d" ]; then + BACKUP_CRON_EXPRESSION="${BACKUP_CRON_EXPRESSION:-@daily}" -echo "Installing cron.d entry with expression $BACKUP_CRON_EXPRESSION." -echo "$BACKUP_CRON_EXPRESSION backup 2>&1" | crontab - + echo "Installing cron.d entry with expression $BACKUP_CRON_EXPRESSION." + echo "$BACKUP_CRON_EXPRESSION backup 2>&1" | crontab - +else + echo "/etc/dockervolumebackup/conf.d was found, using configuration files from this directory." + + for file in /etc/dockervolumebackup/conf.d/*; do + source $file + BACKUP_CRON_EXPRESSION="${BACKUP_CRON_EXPRESSION:-@daily}" + echo "Appending cron.d entry with expression $BACKUP_CRON_EXPRESSION and configuration file $file" + (crontab -l; echo "$BACKUP_CRON_EXPRESSION /bin/sh -c 'set -a; source $file; set +a && backup' 2>&1") | crontab - + done +fi echo "Starting cron in foreground." crond -f -l 8 diff --git a/test/compose/docker-compose.yml b/test/compose/docker-compose.yml index f216b3f..4c1c04e 100644 --- a/test/compose/docker-compose.yml +++ b/test/compose/docker-compose.yml @@ -21,7 +21,7 @@ services: volumes: - webdav_backup_data:/var/lib/dav - backup: &default_backup_service + backup: image: offen/docker-volume-backup:${TEST_VERSION:-canary} hostname: hostnametoken depends_on: diff --git a/test/confd/.gitignore b/test/confd/.gitignore new file mode 100644 index 0000000..4083037 --- /dev/null +++ b/test/confd/.gitignore @@ -0,0 +1 @@ +local diff --git a/test/confd/backup.env b/test/confd/backup.env new file mode 100644 index 0000000..aaeee07 --- /dev/null +++ b/test/confd/backup.env @@ -0,0 +1,2 @@ +BACKUP_FILENAME="conf.tar.gz" +BACKUP_CRON_EXPRESSION="*/1 * * * *" diff --git a/test/confd/docker-compose.yml b/test/confd/docker-compose.yml new file mode 100644 index 0000000..b74bb29 --- /dev/null +++ b/test/confd/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3' + +services: + backup: + image: offen/docker-volume-backup:${TEST_VERSION:-canary} + restart: always + volumes: + - ./local:/archive + - app_data:/backup/app_data:ro + - ./backup.env:/etc/dockervolumebackup/conf.d/00backup.env + - ./never.env:/etc/dockervolumebackup/conf.d/10never.env + - /var/run/docker.sock:/var/run/docker.sock + + offen: + image: offen/offen:latest + labels: + - docker-volume-backup.stop-during-backup=true + volumes: + - app_data:/var/opt/offen + +volumes: + app_data: diff --git a/test/confd/never.env b/test/confd/never.env new file mode 100644 index 0000000..df320c5 --- /dev/null +++ b/test/confd/never.env @@ -0,0 +1,2 @@ +BACKUP_FILENAME="never.tar.gz" +BACKUP_CRON_EXPRESSION="0 0 5 31 2 ?" diff --git a/test/confd/run.sh b/test/confd/run.sh new file mode 100755 index 0000000..7ec9076 --- /dev/null +++ b/test/confd/run.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +set -e + +cd $(dirname $0) + +mkdir -p local + +docker-compose up -d + +# sleep until a backup is guaranteed to have happened on the 1 minute schedule +sleep 100 + +docker-compose down --volumes + +if [ ! -f ./local/conf.tar.gz ]; then + echo "[TEST:FAIL] Config from file was not used." + exit 1 +fi +echo "[TEST:PASS] Config from file was used." + +if [ -f ./local/never.tar.gz ]; then + echo "[TEST:FAIL] Unexpected file was found." + exit 1 +fi +echo "[TEST:PASS] Unexpected cron did not run."