├── .github ├── delete-merged-branch-config.yml ├── dependabot.yml ├── release-drafter.yml └── workflows │ ├── publish.yml │ ├── release-drafter.yml │ └── validate.yml ├── Dockerfile ├── LICENSE.md ├── README.md ├── entrypoint.sh ├── sync-abort.sh └── sync.sh /.github/delete-merged-branch-config.yml: -------------------------------------------------------------------------------- 1 | exclude: 2 | - master 3 | - dev -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" # See documentation for possible values 4 | directory: "/" # Location of package manifests 5 | schedule: 6 | interval: "daily" 7 | time: "06:00" 8 | timezone: "Europe/Stockholm" 9 | labels: 10 | - "dependency" 11 | 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | time: "04:00" 17 | timezone: "Europe/Stockholm" 18 | labels: 19 | - "dependency" -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$RESOLVED_VERSION' 2 | tag-template: 'v$RESOLVED_VERSION' 3 | change-template: '- #$NUMBER $TITLE @$AUTHOR' 4 | sort-direction: ascending 5 | 6 | version-resolver: 7 | major: 8 | labels: 9 | - 'major' 10 | minor: 11 | labels: 12 | - 'minor' 13 | patch: 14 | labels: 15 | - 'patch' 16 | default: patch 17 | 18 | categories: 19 | - title: 'Features' 20 | label: 'feature' 21 | - title: 'Bug Fixes' 22 | labels: 23 | - 'fix' 24 | - 'bugfix' 25 | - 'bug' 26 | 27 | - title: 'Documentation' 28 | label: 'documentation' 29 | 30 | - title: 'Dependencies' 31 | collapse-after: 5 32 | labels: 33 | - 'dependencies' 34 | 35 | autolabeler: 36 | - label: 'documentation' 37 | files: 38 | - '*.md' 39 | branch: 40 | - '/docs\/.+/' 41 | title: 42 | - '/doc/i' 43 | 44 | - label: 'bug' 45 | branch: 46 | - '/fix\/.+/' 47 | title: 48 | - '/fix/i' 49 | 50 | - label: 'enhancement' 51 | branch: 52 | - '/feature\/.+/' 53 | title: 54 | - '/feat/i' 55 | 56 | template: | 57 | ## What’s Changed 58 | 59 | $CHANGES -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | # push: 7 | # branches: ['release'] 8 | 9 | env: 10 | REGISTRY: ghcr.io 11 | IMAGE_NAME: ${{ github.repository }} 12 | 13 | jobs: 14 | build-and-push-image: 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: read 18 | packages: write 19 | 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v4 23 | 24 | - name: Log in to the Container registry 25 | uses: docker/login-action@1f401f745bf57e30b3a2800ad308a87d2ebdf14b 26 | with: 27 | registry: ${{ env.REGISTRY }} 28 | username: ${{ github.actor }} 29 | password: ${{ secrets.GITHUB_TOKEN }} 30 | 31 | - name: Extract metadata (tags, labels) for Docker 32 | id: meta 33 | uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 34 | with: 35 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 36 | 37 | - name: Build and push Docker image 38 | uses: docker/build-push-action@fdf7f43ecf7c1a5c7afe936410233728a8c2d9c2 39 | with: 40 | context: . 41 | push: true 42 | tags: ${{ steps.meta.outputs.tags }} 43 | labels: ${{ steps.meta.outputs.labels }} -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | # pull_request event is required only for autolabeler 9 | pull_request: 10 | # Only following types are handled by the action, but one can default to all as well 11 | types: [opened, reopened, synchronize] 12 | # pull_request_target event is required for autolabeler to support PRs from forks 13 | # pull_request_target: 14 | # types: [opened, reopened, synchronize] 15 | 16 | permissions: 17 | contents: read 18 | 19 | jobs: 20 | update_release_draft: 21 | permissions: 22 | # write permission is required to create a github release 23 | contents: write 24 | # write permission is required for autolabeler 25 | # otherwise, read permission is required at least 26 | pull-requests: write 27 | runs-on: ubuntu-latest 28 | steps: 29 | # Drafts your next Release notes as Pull Requests are merged into "master" 30 | - uses: release-drafter/release-drafter@v5 31 | with: 32 | config-name: release-drafter.yml 33 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 34 | # with: 35 | # config-name: my-config.yml 36 | # disable-autolabeler: true 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: Validate 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | env: 12 | REGISTRY: ghcr.io 13 | IMAGE_NAME: ${{ github.repository }} 14 | 15 | jobs: 16 | build-and-push-image: 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: read 20 | packages: write 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | 26 | - name: Extract metadata (tags, labels) for Docker 27 | id: meta 28 | uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 29 | with: 30 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 31 | 32 | - name: Build Docker image 33 | uses: docker/build-push-action@fdf7f43ecf7c1a5c7afe936410233728a8c2d9c2 34 | with: 35 | context: . 36 | push: false 37 | tags: ${{ steps.meta.outputs.tags }} 38 | labels: ${{ steps.meta.outputs.labels }} 39 | 40 | # - name: Test Docker image 41 | # run: | 42 | # docker run --rm ${{ steps.meta.outputs.tags }} -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | MAINTAINER Robin Ostlund 4 | 5 | ENV INST_RCLONE_VERSION=current 6 | ENV ARCH=amd64 7 | ENV SYNC_SRC= 8 | ENV SYNC_DEST= 9 | ENV SYNC_OPTS=-v 10 | ENV RCLONE_OPTS="--config /config/rclone.conf" 11 | ENV CRON= 12 | ENV CRON_ABORT= 13 | ENV FORCE_SYNC= 14 | ENV CHECK_URL= 15 | ENV TZ= 16 | 17 | RUN apk -U add ca-certificates fuse wget dcron tzdata \ 18 | && rm -rf /var/cache/apk/* \ 19 | && cd /tmp \ 20 | && wget -q http://downloads.rclone.org/rclone-${INST_RCLONE_VERSION}-linux-${ARCH}.zip \ 21 | && unzip /tmp/rclone-${INST_RCLONE_VERSION}-linux-${ARCH}.zip \ 22 | && mv /tmp/rclone-*-linux-${ARCH}/rclone /usr/bin \ 23 | && rm -r /tmp/rclone* 24 | 25 | COPY entrypoint.sh / 26 | COPY sync.sh / 27 | COPY sync-abort.sh / 28 | 29 | VOLUME ["/config"] 30 | 31 | ENTRYPOINT ["/entrypoint.sh"] 32 | 33 | CMD [""] 34 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 Robin Osltlund 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-rclone 2 | # Creds to bcardiff for the work for this docker container (this is a forked version) 3 | 4 | Docker image to perform a [rclone](http://rclone.org) sync based on a cron schedule, with [healthchecks.io](https://healthchecks.io) monitoring. 5 | 6 | rclone is a command line program to sync files and directories to and from: 7 | 8 | * Google Drive 9 | * Amazon S3 10 | * Openstack Swift / Rackspace cloud files / Memset Memstore 11 | * Dropbox 12 | * Google Cloud Storage 13 | * Amazon Drive 14 | * Microsoft OneDrive 15 | * Hubic 16 | * Backblaze B2 17 | * Yandex Disk 18 | * SFTP 19 | * FTP 20 | * HTTP 21 | * The local filesystem 22 | 23 | 24 | ## Usage 25 | 26 | ### Configure rclone 27 | 28 | rclone needs a configuration file where credentials to access different storage 29 | provider are kept. 30 | 31 | By default, this image uses a file `/config/rclone.conf` and a mounted volume may be used to keep that information persisted. 32 | 33 | A first run of the container can help in the creation of the file, but feel free to manually create one. 34 | 35 | ``` 36 | $ mkdir config 37 | $ docker run --rm -it -v $(pwd)/config:/config ghcr.io/robinostlund/docker-rclone-sync:latest 38 | ``` 39 | 40 | ### Perform sync in a daily basis 41 | 42 | A few environment variables allow you to customize the behavior of the sync: 43 | 44 | * `SYNC_SRC` source location for `rclone sync` command 45 | * `SYNC_DEST` destination location for `rclone sync` command 46 | * `CRON` crontab schedule `0 0 * * *` to perform sync every midnight 47 | * `CRON_ABORT` crontab schedule `0 6 * * *` to abort sync at 6am 48 | * `FORCE_SYNC` set variable to perform a sync upon boot 49 | * `CHECK_URL` [healthchecks.io](https://healthchecks.io) url or similar cron monitoring to perform a `GET` after a successful sync 50 | * `SYNC_OPTS` additional options for `rclone sync` command. Defaults to `-v` 51 | * `TZ` set the [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) to use for the cron and log `America/Argentina/Buenos_Aires` 52 | 53 | ```bash 54 | $ docker run --rm -it -v $(pwd)/config:/config -v /path/to/source:/source -e SYNC_SRC="/source" -e SYNC_DEST="dest:path" -e TZ="America/Argentina/Buenos_Aires" -e CRON="0 0 * * *" -e CRON_ABORT="0 6 * * *" -e FORCE_SYNC=1 -e CHECK_URL=https://hchk.io/hchk_uuid ghcr.io/robinostlund/docker-rclone-sync:latest 55 | ``` 56 | 57 | See [rclone sync docs](https://rclone.org/commands/rclone_sync/) for source/dest syntax and additional options. 58 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ ! -z "$TZ" ] 6 | then 7 | cp /usr/share/zoneinfo/$TZ /etc/localtime 8 | echo $TZ > /etc/timezone 9 | fi 10 | 11 | rm -f /tmp/sync.pid 12 | 13 | if [ -z "$SYNC_SRC" ] || [ -z "$SYNC_DEST" ] 14 | then 15 | echo "INFO: No SYNC_SRC and SYNC_DEST found. Starting rclone config" 16 | rclone config $RCLONE_OPTS 17 | echo "INFO: Define SYNC_SRC and SYNC_DEST to start sync process." 18 | else 19 | # SYNC_SRC and SYNC_DEST setup 20 | # run sync either once or in cron depending on CRON 21 | if [ -z "$CRON" ] 22 | then 23 | echo "INFO: No CRON setting found. Running sync once." 24 | echo "INFO: Add CRON=\"0 0 * * *\" to perform sync every midnight" 25 | /sync.sh 26 | else 27 | if [ -z "$FORCE_SYNC" ] 28 | then 29 | echo "INFO: Add FORCE_SYNC=1 to perform a sync upon boot" 30 | else 31 | /sync.sh 32 | fi 33 | 34 | # Setup cron schedule 35 | crontab -d 36 | echo "$CRON /sync.sh >>/tmp/sync.log 2>&1" > /tmp/crontab.tmp 37 | if [ -z "$CRON_ABORT" ] 38 | then 39 | echo "INFO: Add CRON_ABORT=\"0 6 * * *\" to cancel outstanding sync at 6am" 40 | else 41 | echo "$CRON_ABORT /sync-abort.sh >>/tmp/sync.log 2>&1" >> /tmp/crontab.tmp 42 | fi 43 | crontab /tmp/crontab.tmp 44 | rm /tmp/crontab.tmp 45 | 46 | # Start cron 47 | echo "INFO: Starting crond ..." 48 | touch /tmp/sync.log 49 | touch /tmp/crond.log 50 | crond -b -l 0 -L /tmp/crond.log 51 | echo "INFO: crond started" 52 | tail -F /tmp/crond.log /tmp/sync.log 53 | fi 54 | fi 55 | 56 | -------------------------------------------------------------------------------- /sync-abort.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ ! -f /tmp/sync.pid ] 6 | then 7 | echo "INFO: No outstanding sync $(date)" 8 | else 9 | echo "INFO: Stopping sync pid $(cat /tmp/sync.pid) $(date)" 10 | 11 | pkill -P $(cat /tmp/sync.pid) 12 | kill -15 $(cat /tmp/sync.pid) 13 | rm -f /tmp/sync.pid 14 | fi 15 | -------------------------------------------------------------------------------- /sync.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | echo "INFO: Starting sync.sh pid $$ $(date)" 6 | 7 | if [ `lsof | grep $0 | wc -l | tr -d ' '` -gt 1 ] 8 | then 9 | echo "WARNING: A previous sync is still running. Skipping new sync command." 10 | else 11 | 12 | echo $$ > /tmp/sync.pid 13 | 14 | if test "$(rclone ls $SYNC_SRC $RCLONE_OPTS)"; then 15 | # the source directory is not empty 16 | # it can be synced without clear data loss 17 | echo "INFO: Starting rclone sync $SYNC_SRC $SYNC_DEST $RCLONE_OPTS $SYNC_OPTS" 18 | rclone sync $SYNC_SRC $SYNC_DEST $RCLONE_OPTS $SYNC_OPTS 19 | 20 | if [ -z "$CHECK_URL" ] 21 | then 22 | echo "INFO: Define CHECK_URL with https://healthchecks.io to monitor sync job" 23 | else 24 | wget $CHECK_URL -O /dev/null 25 | fi 26 | else 27 | echo "WARNING: Source directory is empty. Skipping sync command." 28 | fi 29 | 30 | rm -f /tmp/sync.pid 31 | 32 | fi 33 | --------------------------------------------------------------------------------