From be7ebc4377afcffcc6aff55cf15191b597acdf2e Mon Sep 17 00:00:00 2001 From: Matt Speer Date: Sat, 3 Oct 2026 11:09:39 -0500 Subject: [PATCH] initial commit for gitea backup script --- gitea_backup.sh | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 gitea_backup.sh diff --git a/gitea_backup.sh b/gitea_backup.sh new file mode 100644 index 0000000..23182b8 --- /dev/null +++ b/gitea_backup.sh @@ -0,0 +1,116 @@ +#!/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 \ No newline at end of file