From c82e74b72b3e8080aca677c4dc15f24cb9ce630d Mon Sep 17 00:00:00 2001 From: Nick Heppler Date: Sat, 3 May 2025 13:42:16 -0400 Subject: [PATCH] feat: add Docker backup script with container lifecycle, compression, and notifications. --- docker_backup.sh | 117 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 docker_backup.sh diff --git a/docker_backup.sh b/docker_backup.sh new file mode 100644 index 0000000..a356cef --- /dev/null +++ b/docker_backup.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +# === Load configuration from .env === +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +if [[ -f "$SCRIPT_DIR/.env" ]]; then + source "$SCRIPT_DIR/.env" +else + echo "[$(date)] โŒ Missing .env file in $SCRIPT_DIR" + exit 1 +fi + +# Ensure backup directory exists +mkdir -p "$BACKUP_DIR" + +# === Timestamp & File Setup === +TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") +OUTPUT_FILE="$BACKUP_DIR/docker_backup_$TIMESTAMP.tar.gz" +LOG_FILE="$BACKUP_DIR/docker_backup_$TIMESTAMP.log" + +# === Logging helper === +log() { + echo "[$(date)] $1" | tee -a "$LOG_FILE" +} + +# === Uptime Kuma Check-in === +uptime_kuma_checkin() { + if [[ -n "$UPTIME_KUMA_URL" && -n "$UPTIME_KUMA_API_KEY" ]]; then + curl -s "${UPTIME_KUMA_URL}/api/push/${UPTIME_KUMA_API_KEY}?status=up&msg=OK" > /dev/null + log "โœ… Uptime Kuma check-in sent." + fi +} + +# === Gotify Error Notification === +gotify_error_notify() { + local summary="Docker backup errors on $(hostname):\n$1" + if [[ -n "$GOTIFY_PUSH_URL" && -n "$GOTIFY_PUSH_KEY" ]]; then + curl -s -X POST "${GOTIFY_PUSH_URL}/message?token=${GOTIFY_PUSH_KEY}" \ + -F "title=[${BACKUP_TAG}] Docker Backup Error on $(hostname)" \ + -F "message=$(echo -e "$summary" | head -c 1000)" \ + -F "priority=5" > /dev/null + log "โš ๏ธ Gotify error notification sent." + fi +} + +# === Stop Containers === +stop_containers() { + log "๐Ÿ›‘ Stopping Docker containers" + for container in ${DOCKER_CONTAINERS[@]}; do + docker stop "$container" >> "$LOG_FILE" 2>&1 + if [[ $? -eq 0 ]]; then + log "โœ… Stopped container: $container" + else + log "โŒ Failed to stop container: $container" + fi + done +} + +# === Start Containers === +start_containers() { + log "๐Ÿš€ Starting Docker containers" + for container in ${DOCKER_CONTAINERS[@]}; do + docker start "$container" >> "$LOG_FILE" 2>&1 + if [[ $? -eq 0 ]]; then + log "โœ… Started container: $container" + else + log "โŒ Failed to start container: $container" + fi + done +} + +# === Backup Directories === +backup_directories() { + log "๐Ÿ“ฆ Backing up directories" + tar -czf "$OUTPUT_FILE" ${DIRECTORIES_TO_BACKUP[@]} >> "$LOG_FILE" 2>&1 + if [[ $? -eq 0 ]]; then + log "โœ… Backup created at $OUTPUT_FILE" + else + log "โŒ Failed to create backup archive" + gotify_error_notify "Backup failed while archiving directories." + exit 1 + fi +} + +# === Cleanup Old Backups and Logs === +cleanup_old_files() { + log "๐Ÿงน Cleaning up files older than $RETENTION_DAYS days" + find "$BACKUP_DIR" \( -name "docker_backup_*.tar.gz" -o -name "docker_backup_*.log" \) -type f -mtime +$RETENTION_DAYS -exec rm -f {} \; +} + +# === Error Scanning === +scan_logs_for_errors() { + log "๐Ÿ” Scanning logs for errors" + ERRORS="" + while IFS= read -r logfile; do + LOG_ERRORS=$(grep -Ei "error|failed|โŒ" "$logfile") + if [[ -n "$LOG_ERRORS" ]]; then + ERRORS+="\n$(basename "$logfile"):\n$LOG_ERRORS\n" + fi + done < <(find "$BACKUP_DIR" -name "docker_backup_*.log" -type f -mtime -$RETENTION_DAYS) + + if [[ -n "$ERRORS" ]]; then + gotify_error_notify "$ERRORS" + fi +} + +# === Run the Backup Process === +log "๐Ÿ”„ Starting Docker backup job" +uptime_kuma_checkin + +stop_containers +backup_directories +start_containers + +cleanup_old_files +scan_logs_for_errors + +log "โœ… Docker backup job completed"