├── LICENSE ├── README.md ├── config.conf ├── extras ├── Pi-hole.md ├── dpkg-installed.md ├── dynamic-xdg-dirs │ ├── post.sh │ └── pre.sh ├── filter.list ├── include-filter │ ├── post.sh │ └── pre.sh ├── rclone-backup.conf ├── rclone-backup@.service └── rclone-backup@.timer ├── filter.list ├── install.sh ├── rclone-backup.service ├── rclone-backup.sh └── rclone-backup.timer /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Jack'lul 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rclone-backup 2 | 3 | Simple script that uses [Rclone](https://rclone.org) to backup your important files to cloud service. 4 | 5 | ## Install 6 | 7 | ```bash 8 | wget -O - https://raw.githubusercontent.com/jacklul/rclone-backup/master/install.sh | sudo bash 9 | ``` 10 | 11 | You must add Rclone remote called `remote` to `/etc/rclone-backup/rclone.conf` (`sudo rclone config --config /etc/rclone-backup/rclone.conf`) or set it with `REMOTE` variable in `/etc/rclone-backup/rclone-backup.conf`. 12 | 13 | Filtering rules are in `/etc/rclone-backup/filter.list` - see [here](https://rclone.org/filtering/) for more information. 14 | 15 | If there is something you need to do before every backup you can create `/etc/rclone-backup/pre.sh` script, it will be executed each time the task starts. 16 | Similarly you can use `/etc/rclone-backup/post.sh` script to do something after backup. 17 | 18 | Any arguments passed to `rclone-backup` are passed to `rclone` command line or you can use `PARAMETERS` variable in `/etc/rclone-backup/rclone-backup.conf`! 19 | -------------------------------------------------------------------------------- /config.conf: -------------------------------------------------------------------------------- 1 | # /etc/rclone-backup/rclone-backup.conf 2 | 3 | #RCLONE_CONFIG="/etc/rclone-backup/rclone.conf" 4 | #FILTER_LIST="/etc/rclone-backup/filter.list" 5 | #BASE_PATH="/" 6 | #REMOTE="remote:" 7 | #PARAMETERS="" 8 | #SCRIPT_PRE="/etc/rclone-backup/script_pre.sh" 9 | #SCRIPT_POST="/etc/rclone-backup/script_post.sh" 10 | -------------------------------------------------------------------------------- /extras/Pi-hole.md: -------------------------------------------------------------------------------- 1 | _Up to date as of Pi-hole v5.2.4._ 2 | 3 | Use this filter list: 4 | 5 | ```text 6 | # Exclude hidden files and directories 7 | - .* 8 | - .*/** 9 | 10 | # Pi-hole 11 | + /etc/pihole/setupVars.conf 12 | + /etc/pihole/pihole-FTL.conf 13 | + /etc/pihole/local.list 14 | + /etc/pihole/custom.list 15 | + /etc/pihole/logrotate 16 | + /etc/dnsmasq.d/*.conf 17 | + /etc/lighttpd/lighttpd.conf 18 | + /etc/cron.d/pihole 19 | + /etc/systemd/system/pihole-FTL.service 20 | + /etc/systemd/system/pihole-FTL.service.d/override.conf 21 | + /etc/pihole/gravity.min.db 22 | 23 | # Unbound 24 | + /etc/unbound/unbound.conf.d/*.conf 25 | + /etc/systemd/system/unbound.service 26 | + /etc/systemd/system/unbound.service.d/override.conf 27 | 28 | # DNSCrypt 29 | + /etc/dnscrypt-proxy/dnscrypt-proxy.toml 30 | + /etc/systemd/system/dnscrypt-proxy.service 31 | + /etc/systemd/system/dnscrypt-proxy.service.d/override.conf 32 | 33 | # Cloudflared 34 | + /etc/cloudflared/config.yaml 35 | + /etc/default/cloudflared 36 | + /etc/systemd/system/cloudflared.service 37 | + /etc/systemd/system/cloudflared.service.d/override.conf 38 | 39 | # This backup script 40 | + /etc/rclone-backup/backup.list 41 | + /etc/rclone-backup/rclone-backup.conf 42 | + /etc/rclone-backup/script.sh 43 | 44 | # User entries should go below this line 45 | 46 | 47 | # Exclude everything else 48 | # DO NOT REMOVE 49 | - ** 50 | ``` 51 | 52 | and this `/etc/rclone-backup/script_pre.sh`: 53 | 54 | ```bash 55 | #!/bin/bash 56 | 57 | PIHOLE_CONFIG_DIR="/etc/pihole" 58 | PIHOLE_GIT_DIR="/etc/.pihole" 59 | GRAVITYDB_FILE="$PIHOLE_CONFIG_DIR/gravity.db" 60 | GRAVITYDB_MIN_FILE="$PIHOLE_CONFIG_DIR/gravity.min.db" 61 | GRAVITYDB_COPYSQL="$PIHOLE_GIT_DIR/advanced/Templates/gravity_copy.sql" 62 | 63 | if [ -f "$GRAVITYDB_FILE" ]; then 64 | command -v sqlite3 >/dev/null 2>&1 || { echo "Please install 'sqlite3' package!"; exit 1; } 65 | command -v md5sum >/dev/null 2>&1 || { echo "Please install 'md5sum' package!"; exit 1; } 66 | 67 | PREVIOUS_CHECKSUM= 68 | if [ -f "$GRAVITYDB_FILE.md5" ]; then 69 | PREVIOUS_CHECKSUM="$(cat "$GRAVITYDB_FILE.md5")" 70 | fi 71 | 72 | CURRENT_CHECKSUM="$(md5sum "$GRAVITYDB_FILE" | awk '{ print $1 }')" 73 | 74 | if [ "$CURRENT_CHECKSUM" != "$PREVIOUS_CHECKSUM" ] || [ ! -f "$GRAVITYDB_MIN_FILE" ]; then 75 | echo "Creating minimal gravity database..." 76 | 77 | sqlite3 "$GRAVITYDB_MIN_FILE" <<< "$(cat "$GRAVITYDB_COPYSQL")" 78 | 79 | if [ ! $? -eq 0 ]; then 80 | echo "Failed to copy gravity database!" 81 | exit 1 82 | fi 83 | 84 | echo "$CURRENT_CHECKSUM" > "$GRAVITYDB_FILE.md5" 85 | fi 86 | fi 87 | ``` 88 | -------------------------------------------------------------------------------- /extras/dpkg-installed.md: -------------------------------------------------------------------------------- 1 | Add this to the filter list: 2 | 3 | ```text 4 | # dpkg installed packages list 5 | + /var/lib/dpkg/installed.txt 6 | ``` 7 | 8 | and this `/etc/rclone-backup/script_pre.sh`: 9 | 10 | ```bash 11 | #!/bin/bash 12 | 13 | PACKAGES_LIST_FILE="/var/lib/dpkg/installed.txt" 14 | PACKAGES_LIST_TMP_FILE="/tmp/dpkg-l.txt" 15 | 16 | command -v md5sum >/dev/null 2>&1 || { echo "Please install 'md5sum' package!"; exit 1; } 17 | 18 | dpkg -l > "$PACKAGES_LIST_TMP_FILE" 19 | 20 | if [ "$(md5sum "$PACKAGES_LIST_TMP_FILE" | awk '{ print $1 }')" != "$(md5sum "$PACKAGES_LIST_FILE" | awk '{ print $1 }')" ]; then 21 | echo "Putting list of installed packages to $PACKAGES_LIST_FILE" 22 | 23 | mv -f "$PACKAGES_LIST_TMP_FILE" "$PACKAGES_LIST_FILE" 24 | else 25 | rm -f "$PACKAGES_LIST_TMP_FILE" 26 | fi 27 | 28 | ``` 29 | -------------------------------------------------------------------------------- /extras/dynamic-xdg-dirs/post.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Remove temporary directory 4 | if [ -d "$TMP_DIR" ] && [[ "$TMP_DIR" == /tmp* ]]; then 5 | rm -fr "$TMP_DIR" 6 | fi 7 | -------------------------------------------------------------------------------- /extras/dynamic-xdg-dirs/pre.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | TMP_DIR="$(mktemp --directory --tmpdir=/tmp "rclone-backup.XXXXXXX")" 4 | [ ! -d "$TMP_DIR" ] && { echo "Failed to create a temporary directory in /tmp"; exit 1; } 5 | 6 | # Generate localized XDG filter list per user when '#include "xdg.list"' is in the filter file 7 | #shellcheck disable=SC1091 8 | if [ -n "$FILTER_LIST" ] && grep -q '^#include "xdg.list"' "$FILTER_LIST"; then 9 | echo "Generating list of XDG user directories..." 10 | 11 | # Hardcoded defaults 12 | DESKTOP=Desktop 13 | DOWNLOAD=Downloads 14 | TEMPLATES=Templates 15 | PUBLICSHARE=Public 16 | DOCUMENTS=Documents 17 | MUSIC=Music 18 | PICTURES=Pictures 19 | VIDEOS=Videos 20 | 21 | # Fetch defaults (xdg-user-dirs package) 22 | [ -f /etc/xdg/user-dirs.defaults ] && source /etc/xdg/user-dirs.defaults 23 | 24 | # Create empty file for the list 25 | :> "$TMP_DIR/xdg.list" 26 | 27 | # Itarate over /home directories and generate list of XDG dirs to include 28 | for dir in /home/*/; do 29 | [[ -L "${dir%/}" || ! -d "${dir%/}" ]] && continue 30 | user=$(basename "$dir") 31 | 32 | echo "Processing for $user..." 33 | 34 | XDG_DESKTOP_DIR="$DESKTOP" 35 | XDG_DOCUMENTS_DIR="$DOCUMENTS" 36 | XDG_DOWNLOAD_DIR="$DOWNLOAD" 37 | XDG_MUSIC_DIR="$MUSIC" 38 | XDG_PICTURES_DIR="$PICTURES" 39 | XDG_PUBLICSHARE_DIR="$PUBLICSHARE" 40 | XDG_TEMPLATES_DIR="$TEMPLATES" 41 | XDG_VIDEOS_DIR="$VIDEOS" 42 | 43 | [ -f "$dir/.config/user-dirs.dirs" ] && source "$dir/.config/user-dirs.dirs" 44 | 45 | XDG_DESKTOP_DIR="${XDG_DESKTOP_DIR/#$HOME\//}" 46 | XDG_DOCUMENTS_DIR="${XDG_DOCUMENTS_DIR/#$HOME\//}" 47 | XDG_DOWNLOAD_DIR="${XDG_DOWNLOAD_DIR/#$HOME\//}" 48 | XDG_MUSIC_DIR="${XDG_MUSIC_DIR/#$HOME\//}" 49 | XDG_PICTURES_DIR="${XDG_PICTURES_DIR/#$HOME\//}" 50 | XDG_PUBLICSHARE_DIR="${XDG_PUBLICSHARE_DIR/#$HOME\//}" 51 | XDG_TEMPLATES_DIR="${XDG_TEMPLATES_DIR/#$HOME\//}" 52 | XDG_VIDEOS_DIR="${XDG_VIDEOS_DIR/#$HOME\//}" 53 | 54 | # Note that Downloads and Public folders are ignored by default 55 | # Modify here to override this 56 | cat >> "$TMP_DIR/xdg.list" <&2 92 | fi 93 | 94 | echo "$line" 95 | done < "$FILTER_LIST" > "$FILTER_LIST_NEW" 96 | 97 | FILTER_LIST="$FILTER_LIST_NEW" 98 | fi 99 | -------------------------------------------------------------------------------- /extras/filter.list: -------------------------------------------------------------------------------- 1 | ################################################## 2 | # Global exclusions 3 | ################################################## 4 | - **/*.log 5 | - **/*log*.txt 6 | - **/*.log.* 7 | - **/*.{tmp,temp} 8 | - **/*.cache 9 | - **/*.part 10 | - **/*.{dmp,dump} 11 | - **/*.crash 12 | - **/*.sock 13 | - /dev/ 14 | - /proc/ 15 | - /run/ 16 | - /sys/ 17 | 18 | ################################################## 19 | # Custom TOP filter 20 | ################################################## 21 | #include "custom-top.list" 22 | 23 | ################################################## 24 | # Important system files 25 | ################################################## 26 | + /etc/rclone-backup/** 27 | #+ /etc/something/important.conf 28 | 29 | ################################################## 30 | # Important user files 31 | ################################################## 32 | + /home/*/.ssh/** 33 | + /home/*/.gnupg/** 34 | 35 | ################################################## 36 | # Global exclusions for home directories 37 | ################################################## 38 | - /home/*/.{tmp,temp}/ 39 | - /home/*/.local/{tmp,temp}/ 40 | - /home/*/.cache/ 41 | - /home/*/.thumbnails/ 42 | - /home/*/.Trash/ 43 | - /home/*/.local/share/Trash/ 44 | - /home/*/.local/mnt/ 45 | - /home/*/.build/ 46 | - /home/*/.{venvs,virtualenvs}/ 47 | - /home/*/.{cargo,rustup}/ 48 | - /home/*/.{dotnet,nuget}/ 49 | - /home/*/.node-gyp/ 50 | - /home/*/.go/ 51 | - /home/*/.platformio/ 52 | + /home/*/.vscode/extensions/*/.vsixmanifest 53 | - /home/*/.vscode/extensions/ 54 | - /home/*/{tmp,temp}/ 55 | - /home/*/node_modules/ 56 | - /home/*/go/ 57 | 58 | ################################################## 59 | # XDG user directories 60 | ################################################## 61 | #include "xdg.list" 62 | 63 | ################################################## 64 | # Configuration directory 65 | ################################################## 66 | - /home/*/.config/**/{Cache,GPUCache,ShaderCache}/ 67 | - /home/*/.config/**/Media Cache/ 68 | - /home/*/.config/**/Application Cache/ 69 | + /home/*/.config/** 70 | 71 | ################################################## 72 | # Application data 73 | ################################################## 74 | + /home/*/.local/share/plasma/** 75 | 76 | ################################################## 77 | # Wine (default prefix) 78 | ################################################## 79 | + /home/*/.wine/drive_c/Users/*/Documents/** 80 | + /home/*/.wine/drive_c/Users/*/Saved Games/** 81 | + /home/*/.wine/*.reg 82 | - /home/*/.wine/** 83 | 84 | ################################################## 85 | # Bottles 86 | ################################################## 87 | + /home/*/.var/app/com.usebottles.bottles/data/bottles/bottles/*/drive_c/Users/*/Documents/** 88 | + /home/*/.var/app/com.usebottles.bottles/data/bottles/bottles/*/drive_c/Users/*/Saved Games/** 89 | - /home/*/.var/app/com.usebottles.bottles/data/bottles/bottles/** 90 | 91 | ################################################## 92 | # Steam 93 | ################################################## 94 | + /home/*/.local/share/Steam/compatdata/*/pfx/drive_c/Users/*/Documents/** 95 | + /home/*/.local/share/Steam/compatdata/*/pfx/drive_c/Users/*/Saved Games/** 96 | #+ /home/*/.local/share/Steam/compatdata/*/pfx/drive_c/Users/*/** 97 | + /home/*/.local/share/Steam/config/** 98 | - /home/*/.local/share/Steam/userdata/**/*cache/ 99 | - /home/*/.local/share/Steam/userdata/*/ugc/*/ 100 | - /home/*/.local/share/Steam/userdata/*/config/grid/ 101 | + /home/*/.local/share/Steam/userdata/** 102 | - /home/*/.local/share/Steam/steamapps/** 103 | 104 | ################################################## 105 | # Flatpak 106 | ################################################## 107 | + /home/*/.var/app/*/config/** 108 | #+ /home/*/.var/app/*/data/** 109 | + /home/*/.local/share/flatpak/overrides/** 110 | - /home/*/.local/share/flatpak/** 111 | + /var/lib/flatpak/app/*/config/** 112 | + /var/lib/flatpak/overrides/** 113 | - /var/lib/flatpak/** 114 | 115 | ################################################## 116 | # Projects/Work directory 117 | ################################################## 118 | # Dependencies storage 119 | - /home/*/{Projects,Work}/**/vendor/** 120 | - /home/*/{Projects,Work}/**/node_modules/** 121 | 122 | # Temporary files/caches/builds 123 | - /home/*/{Projects,Work}/**/.{npm,yarn}/** 124 | - /home/*/{Projects,Work}/**/__pycache__/** 125 | - /home/*/{Projects,Work}/*/{obj,out}/** 126 | - /home/*/{Projects,Work}/*/{var,data}/{cache,log,logs,tmp,temp}/** 127 | 128 | # Git submodules (these can be redownloaded) 129 | - /home/*/{Projects,Work}/**/.git/modules/** 130 | 131 | # Visual Studio 132 | - /home/*/{Projects,Work}/*/.vs/** 133 | 134 | # Unity 135 | - /home/*/{Projects,Work}/*/{Library,Logs,Tmp,Temp}/** 136 | 137 | # Temporary archives on projects etc. 138 | - /home/*/{Projects,Work}/*.{7z,zip,bz2,tar,gz,xz,zst} 139 | 140 | # Include everything else 141 | + /home/*/{Projects,Work}/** 142 | 143 | ################################################## 144 | # Files and hidden directories in home's root 145 | ################################################## 146 | + /home/*/.*/** 147 | + /home/*/*.* 148 | 149 | ################################################## 150 | # Custom BOTTOM filter 151 | ################################################## 152 | #include "custom-bottom.list" 153 | 154 | ################################################## 155 | # Exclude everything else - DO NOT REMOVE! 156 | ################################################## 157 | - ** 158 | -------------------------------------------------------------------------------- /extras/include-filter/post.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Remove temporary directory 4 | if [ -d "$TMP_DIR" ] && [[ "$TMP_DIR" == /tmp* ]]; then 5 | rm -fr "$TMP_DIR" 6 | fi 7 | -------------------------------------------------------------------------------- /extras/include-filter/pre.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | TMP_DIR="$(mktemp --directory --tmpdir=/tmp "rclone-backup.XXXXXXX")" 4 | [ ! -d "$TMP_DIR" ] && { echo "Failed to create a temporary directory in /tmp"; exit 1; } 5 | 6 | # Replace every '#include "FILE"' with the FILE contents 7 | if [ -n "$FILTER_LIST" ] && grep -q '^#include "*"' "$FILTER_LIST"; then 8 | echo "Building filter list with added includes..." 9 | 10 | FILTER_LIST_NEW="$TMP_DIR/filter.list" 11 | 12 | while IFS= read -r line; do 13 | if [[ $line =~ \#include\ \"([^\"]+\.list)\" ]]; then 14 | filename="${BASH_REMATCH[1]}" 15 | 16 | if [[ $filename != /* && -f "$TMP_DIR/$filename" ]]; then 17 | filename="$TMP_DIR/$filename" 18 | fi 19 | 20 | if [ -f "$filename" ]; then 21 | cat "$filename" 22 | [[ $(tail -c1 "$filename" | wc -l) -eq 0 ]] && echo 23 | continue 24 | fi 25 | 26 | echo "File $filename not found - ignoring" >&2 27 | fi 28 | 29 | echo "$line" 30 | done < "$FILTER_LIST" > "$FILTER_LIST_NEW" 31 | 32 | FILTER_LIST="$FILTER_LIST_NEW" 33 | fi 34 | -------------------------------------------------------------------------------- /extras/rclone-backup.conf: -------------------------------------------------------------------------------- 1 | # /home/user/backup/rclone-backup.conf 2 | # 3 | # Call the script like this: 4 | # rclone-backup --config-dir /home/user/backup 5 | # or 6 | # rclone-backup --config /home/user/backup/rclone-backup.conf 7 | # 8 | 9 | RCLONE_CONFIG="/home/user/backup/rclone.conf" 10 | FILTER_LIST="/home/user/backup/filter.list" 11 | BASE_PATH="/home/user" 12 | REMOTE="crypt:" 13 | PARAMETERS="--skip-links --ignore-case --fast-list --transfers 10 --checkers 20" 14 | SCRIPT_PRE="/home/user/backup/script_pre.sh" 15 | SCRIPT_POST="/home/user/backup/script_post.sh" 16 | -------------------------------------------------------------------------------- /extras/rclone-backup@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Create backup using rclone-backup (%i) 3 | After=network-online.target multi-user.target 4 | Wants=network-online.target 5 | 6 | [Service] 7 | Type=simple 8 | ExecStart=/usr/local/bin/rclone-backup --config-dir="%i" 9 | -------------------------------------------------------------------------------- /extras/rclone-backup@.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Daily backup using rclone-backup (%i) 3 | 4 | [Timer] 5 | OnCalendar=daily 6 | Persistent=true 7 | 8 | [Install] 9 | WantedBy=timers.target 10 | -------------------------------------------------------------------------------- /filter.list: -------------------------------------------------------------------------------- 1 | # Exclude hidden files and directories 2 | - .* 3 | - .*/** 4 | 5 | # This script 6 | + /etc/rclone-backup/filter.list 7 | + /etc/rclone-backup/config.conf 8 | + /etc/rclone-backup/*.sh 9 | 10 | # Add your entries here 11 | 12 | # Exclude everything else 13 | # DO NOT REMOVE 14 | - ** 15 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ "$UID" -eq 0 ] || { echo "Admin privileges required"; exit 1; } 4 | 5 | set -e 6 | 7 | SPATH=$(dirname "$0") 8 | REQUIRED_FILES=( rclone-backup.sh rclone-backup.service rclone-backup.timer config.conf filter.list ) 9 | DOWNLOAD_PATH=/tmp/rclone-backup 10 | DOWNLOAD_URL=https://raw.githubusercontent.com/jacklul/rclone-backup/master 11 | 12 | set -e 13 | 14 | MISSING_FILES=0 15 | for FILE in "${REQUIRED_FILES[@]}"; do 16 | if [ ! -f "$SPATH/$FILE" ]; then 17 | MISSING_FILES=$((MISSING_FILES+1)) 18 | fi 19 | done 20 | 21 | if [ "$MISSING_FILES" -gt 0 ]; then 22 | if [ "$MISSING_FILES" = "${#REQUIRED_FILES[@]}" ]; then 23 | mkdir -pv "$DOWNLOAD_PATH" 24 | SPATH="$DOWNLOAD_PATH" 25 | fi 26 | 27 | for FILE in "${REQUIRED_FILES[@]}"; do 28 | if [ ! -f "$SPATH/$FILE" ]; then 29 | wget -nv -O "$SPATH/$FILE" "$DOWNLOAD_URL/$FILE" 30 | fi 31 | done 32 | fi 33 | 34 | for FILE in "${REQUIRED_FILES[@]}"; do 35 | if [ ! -f "$SPATH/$FILE" ]; then 36 | echo "Missing required file for installation: $FILE" 37 | exit 1 38 | fi 39 | done 40 | 41 | cp -v "$SPATH/rclone-backup.sh" /usr/local/bin/rclone-backup && chmod +x /usr/local/bin/rclone-backup 42 | 43 | mkdir -vp /etc/rclone-backup 44 | 45 | if [ ! -f "/etc/rclone-backup/filter.list" ]; then 46 | cp -v "$SPATH/filter.list" /etc/rclone-backup/filter.list 47 | fi 48 | 49 | if [ ! -f "/etc/rclone-backup/config.conf" ]; then 50 | cp -v "$SPATH/config.conf" /etc/rclone-backup/config.conf 51 | fi 52 | 53 | if [ ! -f "/etc/rclone-backup/rclone.conf" ]; then 54 | touch "/etc/rclone-backup/rclone.conf" 55 | chmod 640 "/etc/rclone-backup/rclone.conf" 56 | fi 57 | 58 | mkdir -vp /usr/local/lib/systemd/system 59 | 60 | cp -v "$SPATH/rclone-backup.service" /usr/local/lib/systemd/system && chmod 644 /usr/local/lib/systemd/system/rclone-backup.service 61 | cp -v "$SPATH/rclone-backup.timer" /usr/local/lib/systemd/system && chmod 644 /usr/local/lib/systemd/system/rclone-backup.timer 62 | 63 | echo "Enable the timer with 'systemctl enable --now rclone-backup.timer'" 64 | echo "Run 'sudo rclone config --config /etc/rclone-backup/rclone.conf' and set up a remote with name 'remote'!" 65 | -------------------------------------------------------------------------------- /rclone-backup.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Create backup using rclone-backup 3 | After=network-online.target multi-user.target 4 | Wants=network-online.target 5 | 6 | [Service] 7 | Type=simple 8 | ExecStart=/usr/local/bin/rclone-backup 9 | Restart=no 10 | 11 | # Lower priority 12 | Nice=19 13 | CPUQuota=90% 14 | CPUWeight=50 15 | IOWeight=50 16 | MemorySwapMax=0 17 | CPUSchedulingPolicy=batch 18 | IOSchedulingClass=best-effort 19 | IOSchedulingPriority=7 20 | 21 | # Security hardening 22 | ReadWritePaths=/etc/rclone-backup /var/lock 23 | PrivateTmp=true 24 | ProtectSystem=strict 25 | ProtectKernelModules=yes 26 | ProtectKernelTunables=yes 27 | ProtectControlGroups=yes 28 | RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX 29 | NoNewPrivileges=true 30 | LimitNOFILE=1024 31 | LimitNPROC=64 32 | -------------------------------------------------------------------------------- /rclone-backup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Made by Jack'lul 3 | 4 | command -v rclone >/dev/null 2>&1 || { echo "Please install Rclone first!" >&2; exit 1; } 5 | 6 | positional_args=() 7 | 8 | while [[ $# -gt 0 ]]; do 9 | case $1 in 10 | -d|--config-dir) 11 | CONFIG_DIR="$2" 12 | echo "Using configuration directory: $CONFIG_DIR" 13 | shift 14 | shift 15 | ;; 16 | -c|--config) 17 | new_config_file="$2" 18 | echo "Using configuration file: $new_config_file" 19 | shift 20 | shift 21 | ;; 22 | *) 23 | positional_args+=("$1") 24 | shift 25 | ;; 26 | esac 27 | done 28 | 29 | set -- "${positional_args[@]}" 30 | 31 | CONFIG_DIR="${CONFIG_DIR:-/etc/rclone-backup}" 32 | CONFIG_FILE="${CONFIG_FILE:-$CONFIG_DIR/config.conf}" 33 | RCLONE_CONFIG="$CONFIG_DIR/rclone.conf" 34 | FILTER_LIST="$CONFIG_DIR/filter.list" 35 | BASE_PATH="/" 36 | REMOTE="remote:" 37 | PARAMETERS="" 38 | SCRIPT_PRE="$CONFIG_DIR/pre.sh" 39 | SCRIPT_POST="$CONFIG_DIR/post.sh" 40 | LOCKFILE="/var/lock/$(basename "$0")" 41 | 42 | if [ -n "$new_config_file" ] && [ -f "$new_config_file" ]; then 43 | #shellcheck disable=SC1090 44 | . "$new_config_file" 45 | elif [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then 46 | #shellcheck disable=SC1090 47 | . "$CONFIG_FILE" 48 | fi 49 | 50 | [ -f "$RCLONE_CONFIG" ] || { echo "Missing Rclone configuration: ${RCLONE_CONFIG:-(not set?)}" >&2; exit 1; } 51 | [ -f "$FILTER_LIST" ] || { echo "Missing filter list: ${FILTER_LIST:-(not set?)}" >&2; exit 1; } 52 | 53 | if [ "$UID" -eq 0 ]; then 54 | lockpid=$(cat "$LOCKFILE" 2> /dev/null || echo '') 55 | 56 | if [ -e "$LOCKFILE" ] && [ -n "$lockpid" ] && kill -0 "$lockpid" > /dev/null 2>&1; then 57 | echo "Script is already running!" >&2 58 | exit 6 59 | fi 60 | 61 | echo $$ > "$LOCKFILE" 62 | 63 | function onInterruptOrExit() { 64 | rm "$LOCKFILE" >/dev/null 2>&1 65 | } 66 | trap onInterruptOrExit EXIT 67 | fi 68 | 69 | if [ -n "$SCRIPT_PRE" ] && [ -x "$SCRIPT_PRE" ]; then 70 | echo "Executing script '$SCRIPT_PRE'..." 71 | 72 | #shellcheck disable=SC1090 73 | . "$SCRIPT_PRE" 74 | fi 75 | 76 | # Running via systemd, disable progress and stats if present in the parameters 77 | if [ -n "$INVOCATION_ID" ] || [ -n "$JOURNAL_STREAM" ]; then 78 | PARAMETERS=$(echo "$PARAMETERS " | sed 's/--progress //g' | sed -r 's/--stats [a-zA-Z0-9]+ //g') 79 | fi 80 | 81 | echo "Backing up now..." 82 | 83 | #shellcheck disable=SC2086 84 | rclone sync "$BASE_PATH" "$REMOTE" --config="$RCLONE_CONFIG" --filter-from="$FILTER_LIST" $PARAMETERS "$@" 85 | exitcode=$? 86 | 87 | if [ -n "$SCRIPT_POST" ] && [ -x "$SCRIPT_POST" ]; then 88 | echo "Executing script '$SCRIPT_POST'..." 89 | 90 | #shellcheck disable=SC1090 91 | . "$SCRIPT_POST" "$exitcode" 92 | fi 93 | 94 | if [ "$exitcode" -eq 0 ]; then 95 | echo "Finished successfully" 96 | else 97 | echo "Finished with error code $exitcode" 98 | fi 99 | -------------------------------------------------------------------------------- /rclone-backup.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Scheduled backup using rclone-backup 3 | 4 | [Timer] 5 | OnCalendar=daily 6 | Persistent=true 7 | 8 | [Install] 9 | WantedBy=timers.target 10 | --------------------------------------------------------------------------------