Files
media-server/gitea_backup.sh
T

116 lines
5.4 KiB
Bash

#!/bin/bash
# ==============================================================================
# Script: gitea_backup.sh
# Description: Backs up Gitea (repositories, database, configuration, avatars,
# LFS files, and attachments) to timestamped directories on a mounted
# drive using Gitea's native `gitea dump` command via Docker exec.
# Compatible with non-POSIX filesystems (exFAT, NTFS, FAT32) and
# automatically prunes backups older than RETENTION_DAYS.
# Usage: sudo ./gitea_backup.sh
# Notes: Must be run with root/sudo privileges.
# ==============================================================================
# ==============================================================================
# Define Variables for Source, Destination, and Retention
# ==============================================================================
BACKUP_MOUNT_POINT="/mnt/5TB-Disk1"
GITEA_DATA_DIR="/srv/docker/gitea/data"
GITEA_DESTINATION="$BACKUP_MOUNT_POINT/backup/services/gitea"
CONTAINER_NAME="gitea"
RETENTION_DAYS=10
# Timestamp and Folder Setup
TIMESTAMP=$(date +"%Y-%m-%d_%H%M%S")
CURRENT_BACKUP_DIR="$GITEA_DESTINATION/backup_$TIMESTAMP"
LATEST_TXT_FILE="$GITEA_DESTINATION/latest.txt"
# ntfy Notification Configuration
NTFY_TOPIC="Server"
NTFY_SERVER="https://ntfy.speerfam.net"
NTFY_TOKEN="tk_3xo9mejjtgemyowzckqw2souo3mux"
# ==============================================================================
# Pre-flight Checks
# ==============================================================================
# Check if the mount point directory exists and is a mounted filesystem.
if ! mountpoint -q "$BACKUP_MOUNT_POINT"; then
echo "Error: '$BACKUP_MOUNT_POINT' is not a mount point. Please ensure the drive is mounted." >&2
curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: Script failed. Destination '$BACKUP_MOUNT_POINT' is not mounted." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1
exit 1
fi
# Check if the Gitea data directory exists on the host.
if [ ! -d "$GITEA_DATA_DIR" ]; then
echo "Error: Gitea data directory '$GITEA_DATA_DIR' not found." >&2
curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: Script failed. Gitea data directory '$GITEA_DATA_DIR' not found." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1
exit 1
fi
# Check if the Docker container is running.
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
echo "Error: Docker container '$CONTAINER_NAME' is not running." >&2
curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: Script failed. Docker container '$CONTAINER_NAME' is not running." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1
exit 1
fi
# Create target directory for current run
mkdir -p "$CURRENT_BACKUP_DIR"
# ==============================================================================
# Gitea Backup Execution
# ==============================================================================
echo "Starting Gitea backup ($TIMESTAMP)..."
# Step 1: Trigger in-container gitea dump using official documentation command structure
echo "Executing 'gitea dump' inside container '$CONTAINER_NAME'..."
docker exec -u git -w /tmp "$CONTAINER_NAME" bash -c '/usr/local/bin/gitea dump -c /data/gitea/conf/app.ini -f /data/gitea-dump.zip'
DUMP_STATUS=$?
if [ $DUMP_STATUS -ne 0 ]; then
echo "Error: 'gitea dump' command failed with exit code $DUMP_STATUS." >&2
curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: Gitea internal dump command failed." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1
rm -rf "$CURRENT_BACKUP_DIR"
exit 1
fi
# Step 2: Verify dump file exists on host data volume
GITEA_DUMP_FILE="$GITEA_DATA_DIR/gitea-dump.zip"
if [ ! -f "$GITEA_DUMP_FILE" ]; then
echo "Error: Could not locate generated 'gitea-dump.zip' in '$GITEA_DATA_DIR'." >&2
curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: Gitea backup zip file missing after exec." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1
rm -rf "$CURRENT_BACKUP_DIR"
exit 1
fi
# Step 3: Rsync dump archive to destination directory
echo "Rsyncing Gitea dump archive to '$CURRENT_BACKUP_DIR/gitea-dump.zip'..."
rsync -av --no-owner --no-group "$GITEA_DUMP_FILE" "$CURRENT_BACKUP_DIR/gitea-dump.zip"
RSYNC_STATUS=$?
# Cleanup temporary dump file from host source data directory
rm -f "$GITEA_DUMP_FILE"
if [ $RSYNC_STATUS -ne 0 ]; then
echo "Error: rsync failed to copy Gitea dump archive file." >&2
curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Error: rsync failed during Gitea backup transfer." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1
exit 1
fi
# Step 4: Record current backup path to latest.txt
echo "$CURRENT_BACKUP_DIR" > "$LATEST_TXT_FILE"
# ==============================================================================
# Retention Cleanup (Prune Backups Older than RETENTION_DAYS)
# ==============================================================================
echo "Checking for backups older than $RETENTION_DAYS days..."
find "$GITEA_DESTINATION" -maxdepth 1 -type d -name "backup_*" -mtime +$RETENTION_DAYS -exec rm -rf {} +
# ==============================================================================
# Completion and Notification
# ==============================================================================
echo "Success: Gitea backup completed at '$CURRENT_BACKUP_DIR'."
curl -H "Authorization: Bearer $NTFY_TOKEN" -d "Success: Gitea backup completed ($TIMESTAMP). Retaining $RETENTION_DAYS days." "$NTFY_SERVER/$NTFY_TOPIC" > /dev/null 2>&1