├── .github └── FUNDING.yml ├── Images └── usage.png ├── LICENSE ├── README.md ├── backup_docker_compose_containers.sh ├── backup_docker_compose_containers_tronitor.sh └── docker_container_healthchecks.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: tronyx # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /Images/usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tronyx/docker-scripts/b2a3fa5625d8d87ea513e0205474f138deb1ff39/Images/usage.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Chris Yocum 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 | # Docker Scripts 2 | 3 | Various Bash scripts to work with Docker containers. 4 | 5 | ## Backup/Update Scripts 6 | 7 | ### Usage 8 | 9 | ![Script Usage](/Images/usage.png) 10 | 11 | I utilize the CloudFlare maintenance page setup from [gilbN](https://github.com/gilbN) that is outlined [HERE](https://technicalramblings.com/blog/how-to-setup-a-cloudflare-worker-to-show-a-maintenance-page-when-ca-backup-plugin-is-running/). If you do not use this you will want to comment out the following lines of the script: 12 | 13 | ``` 14 | 148 echo 'Enabling CloudFlare maintenance page...' 15 | 149 /root/scripts/start_maint.sh 16 | 17 | 237 stop_maint 18 | ``` 19 | 20 | The script is setup so that you can have it send a Discord notification on maintenance start and maintenance completion, with a status message, or it can send you a text message as well. The correspondding variables at the top of the script will need to be filled in/modified: 21 | 22 | ``` 23 | 20 # Set your notification type 24 | 21 discord='false' 25 | 22 text='false' 26 | 23 # Set to true if you want a notification at the start of the maintenance 27 | 24 notifyStart='false' 28 | 25 # Set your Discord webhook URL if you set discord to true 29 | 26 webhookURL='' 30 | 27 # Define your SMS e-mail address (AT&T as an example) if you set text to true 31 | 28 smsAddress='5551234567@txt.att.net' 32 | ``` 33 | 34 | ### Backup Docker Compose Containers 35 | 36 | Script to update, backup, or update and backup your Docker-Compose containers. 37 | 38 | ### Backup Docker Compose Containers w/ Application Healthchecks, Tronitor, & Docker Container Healthchecks Integration 39 | 40 | Designed to be used with my [HealthChecks - Linux](https://github.com/tronyx/HealthChecks-Linux) and the below outlined Docker Container Healthchecks scripts. If you're not using one, you will want to comment out the corresponding lines in the script or make some other modifications so that no errors occur when the script is ran. 41 | 42 | Script to update, backup, or update and backup your Docker-Compose containers that includes integration with my [Tronitor](https://github.com/christronyxyocum/tronitor) script that allows you to pause and unpause your HealthChecks.io, UptimeRobot, or StatusCake monitors manually or via a cronjob for scheduled maintenance, etc. 43 | 44 | You will want to comment out/remove the lines for any monitoring provider that you do not use. 45 | 46 | ## Docker Container Healthchecks 47 | 48 | Script to check that your Docker containers are running and, if not, send a message to Discord/Slack. Designed to be ran as a cronjob on the Linux Host that your Docker containers are running on. 49 | 50 | ## Example Cronjobs 51 | 52 | ### Update/Backup Script 53 | 54 | Here is the cronjob that I use to run the update/backup script every Sunday morning at 5am: 55 | 56 | ```bash 57 | ## Backup and update Docker Compose containers every Sunday morning 58 | 0 5 * * 0 /home/tronyx/scripts/backup_docker_compose_containers.sh -a > /var/log/backup.log 59 | ``` 60 | 61 | The `> /var/log/backup.log` at the end allows me to log the output of the script so I can check it if there was an issue while it ran overnight. 62 | 63 | ### Docker Container Healthchecks Script 64 | 65 | Here's the cronjob that I use to run the script every five minutes: 66 | 67 | ```bash 68 | ## Run Docker container healthcheck script 69 | */5 * * * * /home/tronyx/scripts/docker_container_healthchecks.sh 70 | ``` -------------------------------------------------------------------------------- /backup_docker_compose_containers.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Backup Docker app dirs, update images, and rebuild containers w/ Docker-Compose 4 | # Tronyx 5 | set -eo pipefail 6 | IFS=$'\n\t' 7 | 8 | # Define some vars 9 | tempDir='/tmp/tronitor/' 10 | composeFile='/home/docker-compose.yml' 11 | containerNamesFile="${tempDir}container_names.txt" 12 | appdataDirectory='/home/' 13 | backupDirectory='/mnt/docker_backup/' 14 | today=$(date +%Y-%m-%d) 15 | days=$(( ( $(date '+%s') - $(date -d '2 months ago' '+%s') ) / 86400 )) 16 | domain='domain.com' 17 | # Set your notification type 18 | discord='false' 19 | text='false' 20 | # Set to true if you want a notification at the start of the maintenance 21 | notifyStart='false' 22 | # Set your Discord webhook URL if you set discord to true 23 | webhookURL='' 24 | # Define your SMS e-mail address (AT&T as an example) if you set text to true 25 | smsAddress='5551234567@txt.att.net' 26 | # Exclude containers, IE: 27 | # ("plex" "sonarr" "radarr" "lidarr") 28 | exclude=("sonarr") 29 | # Arguments 30 | readonly args=("$@") 31 | # Colors 32 | readonly grn='\e[32m' 33 | readonly red='\e[31m' 34 | readonly lorg='\e[38;5;130m' 35 | readonly endColor='\e[0m' 36 | 37 | # Define usage and script options 38 | usage() { 39 | cat <<- EOF 40 | 41 | Usage: $(echo -e "${lorg}$0${endColor}") $(echo -e "${grn}"-[OPTION]"${endColor}") 42 | 43 | $(echo -e "${grn}"-b/--backup"${endColor}""${endColor}") Backup all Docker containers. 44 | $(echo -e "${grn}"-u/--update"${endColor}") Update all Docker containers. 45 | $(echo -e "${grn}"-a/--all"${endColor}") Backup and update all Docker containers. 46 | $(echo -e "${grn}"-h/--help"${endColor}") Display this usage dialog. 47 | 48 | EOF 49 | 50 | } 51 | 52 | # Define script options 53 | cmdline() { 54 | local arg= 55 | local local_args 56 | local OPTERR=0 57 | for arg; do 58 | local delim="" 59 | case "${arg}" in 60 | # Translate --gnu-long-options to -g (short options) 61 | --backup) local_args="${local_args}-b " ;; 62 | --update) local_args="${local_args}-u " ;; 63 | --all) local_args="${local_args}-a " ;; 64 | --help) local_args="${local_args}-h " ;; 65 | # Pass through anything else 66 | *) 67 | [[ ${arg:0:1} == "-" ]] || delim='"' 68 | local_args="${local_args:-}${delim}${arg}${delim} " 69 | ;; 70 | esac 71 | done 72 | 73 | # Reset the positional parameters to the short options 74 | eval set -- "${local_args:-}" 75 | 76 | while getopts "hbua" OPTION; do 77 | case "$OPTION" in 78 | b) 79 | backup=true 80 | ;; 81 | u) 82 | update=true 83 | ;; 84 | a) 85 | all=true 86 | ;; 87 | h) 88 | usage 89 | exit 90 | ;; 91 | *) 92 | echo -e "${red}You are specifying a non-existent option!${endColor}" 93 | usage 94 | exit 95 | ;; 96 | esac 97 | done 98 | return 0 99 | } 100 | 101 | # Script Information 102 | get_scriptname() { 103 | local source 104 | local dir 105 | source="${BASH_SOURCE[0]}" 106 | while [[ -L ${source} ]]; do 107 | dir="$(cd -P "$(dirname "${source}")" > /dev/null && pwd)" 108 | source="$(readlink "${source}")" 109 | [[ ${source} != /* ]] && source="${dir}/${source}" 110 | done 111 | echo "${source}" 112 | } 113 | 114 | readonly scriptname="$(get_scriptname)" 115 | 116 | # Check whether or not user is root or used sudo 117 | root_check() { 118 | if [[ ${EUID} -ne 0 ]]; then 119 | echo -e "${red}You didn't run the script as root!${endColor}" 120 | echo -e "${red}Doing it for you now...${endColor}" 121 | echo '' 122 | sudo bash "${scriptname:-}" "${args[@]:-}" 123 | exit 124 | fi 125 | } 126 | 127 | # Check for empty arg 128 | check_empty_arg() { 129 | for arg in "${args[@]:-}"; do 130 | if [ -z "${arg}" ]; then 131 | usage 132 | exit 133 | fi 134 | done 135 | } 136 | 137 | # Function to check that the webhook URL is defined if discord is set to true. 138 | check_webhook_url() { 139 | if [[ -z ${webhookURL} ]] && [[ ${discord} == 'true' ]]; then 140 | echo -e "${red}You didn't define your Discord webhook URL, exiting!${endColor}" 141 | exit 1 142 | fi 143 | } 144 | 145 | # Function to enable CloudFlare maintenance page and notify of maintenance 146 | # start, if notifyStart set to true 147 | # Adjust or comment out the path to the CF maintenance page script 148 | start_maint() { 149 | if [[ ${notifyStart} == 'true' ]]; then 150 | if [[ ${discord} == 'true' ]]; then 151 | curl -s -H "Content-Type: application/json" -X POST -d '{"embeds": [{"title": "Docker Compose Backup/Update Started!", "description": "The scheduled run of the Docker Compose containers update/backup script has started.", "color": 39219}]}' "${webhookURL}" 152 | fi 153 | fi 154 | echo 'Enabling CloudFlare maintenance page...' 155 | /root/scripts/start_maint.sh 156 | } 157 | 158 | # Update Docker images 159 | update_images() { 160 | echo 'Updating Docker images...' 161 | COMPOSE_HTTP_TIMEOUT=900 COMPOSE_PARALLEL_LIMIT=25 /usr/local/bin/docker-compose -f "${composeFile}" pull -q --ignore-pull-failures >/dev/null 2>&1 || echo "There was an issues pulling the image for one or more container. Run a pull manually to determine which containers are problematic." 162 | } 163 | 164 | # Create list of container names 165 | create_containers_list() { 166 | echo 'Creating list of Docker containers...' 167 | /usr/local/bin/docker-compose -f "${composeFile}" config --services | sort > "${containerNamesFile}" 168 | } 169 | 170 | # Stop containers 171 | compose_down() { 172 | echo 'Performing docker-compose down...' 173 | COMPOSE_HTTP_TIMEOUT=900 COMPOSE_PARALLEL_LIMIT=25 /usr/local/bin/docker-compose -f "${composeFile}" down 174 | } 175 | 176 | # Loop through all containers to backup appdata dirs 177 | backup() { 178 | while IFS= read -r CONTAINER; do 179 | if [[ ! ${exclude[*]} =~ ${CONTAINER} ]]; then 180 | echo "Backing up ${CONTAINER}..." 181 | tar czf "${backupDirectory}""${CONTAINER}"-"${today}".tar.gz -C "${appdataDirectory}" "${CONTAINER}"/ 182 | fi 183 | done < <(cat "${containerNamesFile}") 184 | } 185 | 186 | # Start containers and sleep to make sure they have time to startup 187 | compose_up() { 188 | echo 'Performing docker-compose up...' 189 | COMPOSE_HTTP_TIMEOUT=900 COMPOSE_PARALLEL_LIMIT=25 /usr/local/bin/docker-compose -f "${composeFile}" up -d --no-color 190 | echo 'Sleeping for 5 minutes to allow the containers to start...' 191 | sleep 300 192 | } 193 | 194 | # Function to disable CloudFlare maintenance page 195 | # Adjust or comment out the path to the CF maintenance page script 196 | stop_maint() { 197 | echo 'Disabling CloudFlare maintenance page...' 198 | /root/scripts/stop_maint.sh 199 | } 200 | 201 | # Unpause monitors if TronFlix status is 200 202 | unpause_check(){ 203 | echo 'Sleeping for 5 minutes to allow the applications to finish starting...' 204 | sleep 300 205 | echo 'Performing monitor unpause check...' 206 | domainStatus=$(curl -sI https://"${domain}" | grep -i http/ |awk '{print $2}') 207 | domainCurl=$(curl -sI https://"${domain}" | head -2) 208 | if [ "${domainStatus}" == 200 ]; then 209 | echo 'Success!' 210 | stop_maint 211 | if [[ ${discord} == 'true' ]]; then 212 | curl -s -H "Content-Type: application/json" -X POST -d '{"embeds": [{"title": "Docker Compose Backup/Update Completed!", "description": "The scheduled run of the Docker Compose containers backup/update was successful. The specified Domain responded with an HTTP status of 200 after the containers were brought back online.", "color": 39219}]}' "${webhookURL}" 213 | fi 214 | else 215 | if [[ ${text} == 'true' ]]; then 216 | echo "${domainCurl}" |mutt -s "${domain} is still down after weekly backup!" "${smsAddress}" 217 | elif [[ ${discord} == 'true' ]]; then 218 | curl -s -H "Content-Type: application/json" -X POST -d '{"embeds": [{"title": "Docker Compose Backup/Update Failed!", "description": "The scheduled run of the Docker Compose containers backup/update has failed! The specified Domain did NOT respond with an HTTP status of 200 after the containers were brought back online!", "color": 16711680}]}' "${webhookURL}" 219 | fi 220 | fi 221 | } 222 | 223 | # Cleanup backups older than two months and perform docker prune 224 | cleanup(){ 225 | echo 'Removing old backups and performing docker prune...' 226 | find "${backupDirectory}"*.tar.gz -mtime +"${days}" -type f -delete 227 | docker system prune -f -a --volumes 228 | } 229 | 230 | main(){ 231 | root_check 232 | cmdline "${args[@]:-}" 233 | check_empty_arg 234 | check_webhook_url 235 | if [ "${backup}" = 'true' ]; then 236 | start_maint 237 | create_lock_files 238 | create_containers_list 239 | compose_down 240 | backup 241 | backup_env_files 242 | compose_up 243 | unpause_check 244 | cleanup 245 | elif [ "${update}" = 'true' ]; then 246 | start_maint 247 | create_lock_files 248 | update_images 249 | create_containers_list 250 | compose_up 251 | unpause_check 252 | cleanup 253 | elif [ "${all}" = 'true' ]; then 254 | start_maint 255 | create_lock_files 256 | update_images 257 | create_containers_list 258 | compose_down 259 | backup 260 | backup_env_files 261 | compose_up 262 | unpause_check 263 | cleanup 264 | fi 265 | } 266 | 267 | main -------------------------------------------------------------------------------- /backup_docker_compose_containers_tronitor.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Backup Docker app dirs, update images, and rebuild containers w/ Docker-Compose 4 | # Tronyx 5 | set -eo pipefail 6 | IFS=$'\n\t' 7 | 8 | # Define some vars 9 | tempDir='/tmp/tronitor/' 10 | healthchecksLockFile="${tempDir}healthchecks.lock" 11 | dockerChecksLockFile="${tempDir}docker_checks.lock" 12 | composeFile='/home/docker-compose.yml' 13 | containerNamesFile="${tempDir}container_names.txt" 14 | appdataDirectory='/home/' 15 | backupDirectory='/mnt/docker_backup/' 16 | tronitorDirectory='/home/tronyx/scripts/tronitor/' 17 | today=$(date +%Y-%m-%d) 18 | days=$(( ( $(date '+%s') - $(date -d '2 months ago' '+%s') ) / 86400 )) 19 | domain='tronflix.app' 20 | # Set your notification type 21 | discord='false' 22 | text='false' 23 | # Set to true if you want a notification at the start of the maintenance 24 | notifyStart='false' 25 | # Set your Discord webhook URL if you set discord to true 26 | webhookURL='' 27 | # Define your SMS e-mail address (AT&T as an example) if you set text to true 28 | smsAddress='5551234567@txt.att.net' 29 | # Exclude containers, IE: 30 | # ("plex" "sonarr" "radarr" "lidarr") 31 | exclude=("sonarr") 32 | # Arguments 33 | readonly args=("$@") 34 | # Colors 35 | readonly grn='\e[32m' 36 | readonly red='\e[31m' 37 | readonly lorg='\e[38;5;130m' 38 | readonly endColor='\e[0m' 39 | 40 | # Define usage and script options 41 | usage() { 42 | cat <<- EOF 43 | 44 | Usage: $(echo -e "${lorg}$0${endColor}") $(echo -e "${grn}"-[OPTION]"${endColor}") 45 | 46 | $(echo -e "${grn}"-b/--backup"${endColor}""${endColor}") Backup all Docker containers. 47 | $(echo -e "${grn}"-u/--update"${endColor}") Update all Docker containers. 48 | $(echo -e "${grn}"-a/--all"${endColor}") Backup and update all Docker containers. 49 | $(echo -e "${grn}"-h/--help"${endColor}") Display this usage dialog. 50 | 51 | EOF 52 | 53 | } 54 | 55 | # Define script options 56 | cmdline() { 57 | local arg= 58 | local local_args 59 | local OPTERR=0 60 | for arg; do 61 | local delim="" 62 | case "${arg}" in 63 | # Translate --gnu-long-options to -g (short options) 64 | --backup) local_args="${local_args}-b " ;; 65 | --update) local_args="${local_args}-u " ;; 66 | --all) local_args="${local_args}-a " ;; 67 | --help) local_args="${local_args}-h " ;; 68 | # Pass through anything else 69 | *) 70 | [[ ${arg:0:1} == "-" ]] || delim='"' 71 | local_args="${local_args:-}${delim}${arg}${delim} " 72 | ;; 73 | esac 74 | done 75 | 76 | # Reset the positional parameters to the short options 77 | eval set -- "${local_args:-}" 78 | 79 | while getopts "hbua" OPTION; do 80 | case "$OPTION" in 81 | b) 82 | backup=true 83 | ;; 84 | u) 85 | update=true 86 | ;; 87 | a) 88 | all=true 89 | ;; 90 | h) 91 | usage 92 | exit 93 | ;; 94 | *) 95 | echo -e "${red}You are specifying a non-existent option!${endColor}" 96 | usage 97 | exit 98 | ;; 99 | esac 100 | done 101 | return 0 102 | } 103 | 104 | # Script Information 105 | get_scriptname() { 106 | local source 107 | local dir 108 | source="${BASH_SOURCE[0]}" 109 | while [[ -L ${source} ]]; do 110 | dir="$(cd -P "$(dirname "${source}")" > /dev/null && pwd)" 111 | source="$(readlink "${source}")" 112 | [[ ${source} != /* ]] && source="${dir}/${source}" 113 | done 114 | echo "${source}" 115 | } 116 | 117 | readonly scriptname="$(get_scriptname)" 118 | 119 | # Check whether or not user is root or used sudo 120 | root_check() { 121 | if [[ ${EUID} -ne 0 ]]; then 122 | echo -e "${red}You didn't run the script as root!${endColor}" 123 | echo -e "${red}Doing it for you now...${endColor}" 124 | echo '' 125 | sudo bash "${scriptname:-}" "${args[@]:-}" 126 | exit 127 | fi 128 | } 129 | 130 | # Check for empty arg 131 | check_empty_arg() { 132 | for arg in "${args[@]:-}"; do 133 | if [ -z "${arg}" ]; then 134 | usage 135 | exit 136 | fi 137 | done 138 | } 139 | 140 | # Function to check that the webhook URL is defined if discord is set to true. 141 | check_webhook_url() { 142 | if [[ -z ${webhookURL} ]] && [[ ${discord} == 'true' ]]; then 143 | echo -e "${red}You didn't define your Discord webhook URL, exiting!${endColor}" 144 | exit 1 145 | fi 146 | } 147 | 148 | # Function to enable CloudFlare maintenance page and notify of maintenance 149 | # start, if notifyStart set to true 150 | # Adjust or comment out the path to the CF maintenance page script 151 | start_maint() { 152 | if [[ ${notifyStart} == 'true' ]]; then 153 | if [[ ${discord} == 'true' ]]; then 154 | curl -s -H "Content-Type: application/json" -X POST -d '{"embeds": [{"title": "Docker Compose Backup/Update Started!", "description": "The scheduled run of the Docker Compose containers update/backup script has started.", "color": 39219}]}' "${webhookURL}" 155 | fi 156 | fi 157 | echo 'Enabling CloudFlare maintenance page...' 158 | /root/scripts/start_maint.sh 159 | } 160 | 161 | # Function to create lockfile for pausing application and container checks 162 | create_lock_files() { 163 | echo 'Creating lock files...' 164 | true > "${healthchecksLockFile}" 165 | true > "${dockerChecksLockFile}" 166 | } 167 | 168 | # Pause monitors with Tronitor 169 | pause_all_monitors() { 170 | # Comment out what you do not use 171 | echo 'Pausing Healthchecks.io monitors...' 172 | "${tronitorDirectory}"tronitor_no_jq.sh -m hc -p all 173 | echo 'Pausing UptimeRobot monitors...' 174 | "${tronitorDirectory}"tronitor_no_jq.sh -m ur -p all 175 | echo 'Pausing Upptime monitors...' 176 | "${tronitorDirectory}"tronitor_no_jq.sh -m up -p all 177 | } 178 | 179 | # Update Docker images 180 | update_images() { 181 | echo 'Updating Docker images...' 182 | COMPOSE_HTTP_TIMEOUT=900 COMPOSE_PARALLEL_LIMIT=25 /usr/local/bin/docker-compose -f "${composeFile}" pull -q --ignore-pull-failures >/dev/null 2>&1 || echo "There was an issues pulling the image for one or more container. Run a pull manually to determine which containers are problematic." 183 | } 184 | 185 | # Create list of container names 186 | create_containers_list() { 187 | echo 'Creating list of Docker containers...' 188 | /usr/local/bin/docker-compose -f "${composeFile}" config --services | sort > "${containerNamesFile}" 189 | } 190 | 191 | # Stop containers 192 | compose_down() { 193 | echo 'Performing docker-compose down...' 194 | COMPOSE_HTTP_TIMEOUT=900 COMPOSE_PARALLEL_LIMIT=25 /usr/local/bin/docker-compose -f "${composeFile}" down 195 | } 196 | 197 | # Loop through all containers to backup appdata dirs 198 | backup() { 199 | while IFS= read -r CONTAINER; do 200 | if [[ ! ${exclude[*]} =~ ${CONTAINER} ]]; then 201 | echo "Backing up ${CONTAINER}..." 202 | tar czf "${backupDirectory}""${CONTAINER}"-"${today}".tar.gz -C "${appdataDirectory}" "${CONTAINER}"/ 203 | fi 204 | done < <(cat "${containerNamesFile}") 205 | } 206 | 207 | # Start containers and sleep to make sure they have time to startup 208 | compose_up() { 209 | echo 'Performing docker-compose up...' 210 | COMPOSE_HTTP_TIMEOUT=900 COMPOSE_PARALLEL_LIMIT=25 /usr/local/bin/docker-compose -f "${composeFile}" up -d --no-color 211 | echo 'Sleeping for 5 minutes to allow the containers to start...' 212 | sleep 300 213 | } 214 | 215 | # Function to disable CloudFlare maintenance page 216 | # Adjust or comment out the path to the CF maintenance page script 217 | stop_maint() { 218 | echo 'Disabling CloudFlare maintenance page...' 219 | /root/scripts/stop_maint.sh 220 | } 221 | 222 | # Unpause monitors if TronFlix status is 200 223 | unpause_check(){ 224 | echo 'Sleeping for 5 minutes to allow the applications to finish starting...' 225 | sleep 300 226 | echo 'Performing monitor unpause check...' 227 | domainStatus=$(curl -sI https://"${domain}" | grep -i http/ |awk '{print $2}') 228 | domainCurl=$(curl -sI https://"${domain}" | head -2) 229 | if [ "${domainStatus}" == 200 ]; then 230 | echo 'Success!' 231 | # Comment out what you do not need 232 | echo 'Unpausing HealthChecks.io monitors...' 233 | "${tronitorDirectory}"tronitor_no_jq.sh -m hc -u all 234 | echo 'Unpausing UptimeRobot monitors...' 235 | "${tronitorDirectory}"tronitor_no_jq.sh -m ur -u all 236 | echo 'Unpausing Upptime monitors...' 237 | "${tronitorDirectory}"tronitor_no_jq.sh -m up -u all 238 | echo 'Removing lock files...' 239 | rm -rf "${healthchecksLockFile}" 240 | rm -rf "${dockerChecksLockFile}" 241 | stop_maint 242 | if [[ ${discord} == 'true' ]]; then 243 | curl -s -H "Content-Type: application/json" -X POST -d '{"embeds": [{"title": "Docker Compose Backup/Update Completed!", "description": "The scheduled run of the Docker Compose containers backup/update was successful. The specified Domain responded with an HTTP status of 200 after the containers were brought back online.", "color": 39219}]}' "${webhookURL}" 244 | fi 245 | else 246 | if [[ ${text} == 'true' ]]; then 247 | echo "${domainCurl}" |mutt -s "${domain} is still down after weekly backup!" "${smsAddress}" 248 | elif [[ ${discord} == 'true' ]]; then 249 | curl -s -H "Content-Type: application/json" -X POST -d '{"embeds": [{"title": "Docker Compose Backup/Update Failed!", "description": "The scheduled run of the Docker Compose containers backup/update has failed! The specified Domain did NOT respond with an HTTP status of 200 after the containers were brought back online!", "color": 16711680}]}' "${webhookURL}" 250 | fi 251 | fi 252 | } 253 | 254 | # Cleanup backups older than two months and perform docker prune 255 | cleanup(){ 256 | echo 'Removing old backups and performing docker prune...' 257 | find "${backupDirectory}"*.tar.gz -mtime +"${days}" -type f -delete 258 | docker system prune -f -a --volumes 259 | } 260 | 261 | main(){ 262 | root_check 263 | cmdline "${args[@]:-}" 264 | check_empty_arg 265 | check_webhook_url 266 | if [ "${backup}" = 'true' ]; then 267 | start_maint 268 | create_containers_list 269 | compose_down 270 | backup 271 | compose_up 272 | unpause_check 273 | cleanup 274 | elif [ "${update}" = 'true' ]; then 275 | start_maint 276 | update_images 277 | create_containers_list 278 | compose_up 279 | unpause_check 280 | cleanup 281 | elif [ "${all}" = 'true' ]; then 282 | start_maint 283 | update_images 284 | create_containers_list 285 | compose_down 286 | backup 287 | compose_up 288 | unpause_check 289 | cleanup 290 | fi 291 | } 292 | 293 | main -------------------------------------------------------------------------------- /docker_container_healthchecks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Script to check container status and report to Discord if any of them are not running 4 | # Tronyx 5 | 6 | # Define some variables 7 | tempDir='/tmp/' 8 | containerNamesFile="${tempDir}container_names.txt" 9 | # Exclude containers you do not want to be checked 10 | exclude=("container-1" "container-2" "container-3") 11 | # Your webhook URL for the Discord channel you want alerts sent to 12 | discordWebhookURL='' 13 | # Your Discord numeric user ID 14 | # To find your user ID just type \@ or \@, like so \@username#1337 15 | # It will look something like <@123492578063015834> and you NEED the exclamation point like below 16 | discordUserID='<@!123492578063015834>' 17 | 18 | # Function to create list of Docker containers 19 | create_containers_list() { 20 | docker ps -a --format '{{.Names}}' | sort > "${containerNamesFile}" 21 | } 22 | 23 | # Function to check Docker containers 24 | check_containers() { 25 | if [ -f ${containerNamesFile} ]; then 26 | if [ -s ${containerNamesFile} ]; then 27 | while IFS= read -r container; do 28 | if [[ ! ${exclude[*]} =~ ${container} ]]; then 29 | containerStatus=$(docker inspect "${container}" | jq .[].State.Status | tr -d '"') 30 | if [ "${containerStatus}" = 'running' ];then 31 | : 32 | elif [ "${containerStatus}" = 'exited' ];then 33 | curl -s -H "Content-Type: application/json" -X POST -d '{"content": "'"${discordUserID}"' The '"${container}"' container is currently stopped!"}' "${discordWebhookURL}" 34 | elif [ "${containerStatus}" = 'dead' ];then 35 | curl -s -H "Content-Type: application/json" -X POST -d '{"content": "'"${discordUserID}"' The '"${container}"' container is currently dead!"}' "${discordWebhookURL}" 36 | elif [ "${containerStatus}" = 'restarting' ];then 37 | curl -s -H "Content-Type: application/json" -X POST -d '{"content": "'"${discordUserID}"' The '"${container}"' container is currently restarting!"}' "${discordWebhookURL}" 38 | else 39 | curl -s -H "Content-Type: application/json" -X POST -d '{"content": "'"${discordUserID}"' The '"${container}"' container currently has an unknown status!"}' "${discordWebhookURL}" 40 | fi 41 | fi 42 | done < <(cat "${containerNamesFile}") 43 | else 44 | echo "There are currently no Docker containers on this Server!" 45 | exit 0 46 | fi 47 | else 48 | echo "Unable to find ${containerNamesFile}!" 49 | exit 1 50 | fi 51 | } 52 | 53 | # Main function to run all other functions 54 | main() { 55 | create_containers_list 56 | check_containers 57 | } 58 | 59 | main 60 | --------------------------------------------------------------------------------