97 lines
3.1 KiB
Django/Jinja
97 lines
3.1 KiB
Django/Jinja
#!/bin/bash
|
|
#
|
|
# Minecraft Server Backup Script
|
|
# Deployed by Ansible - {{ ansible_date_time.iso8601 if ansible_date_time is defined else 'managed' }}
|
|
#
|
|
# Creates rolling backups of the Minecraft world and data directories.
|
|
# Prunes old backups based on retention count.
|
|
#
|
|
|
|
set -euo pipefail
|
|
|
|
# Configuration
|
|
DATA_DIR="{{ paths.data_dir }}"
|
|
BACKUP_DIR="{{ paths.backup_dir }}"
|
|
RETENTION_COUNT="{{ backup.retention_count | default(10) }}"
|
|
COMPRESS="{{ backup.compress | default('true') }}"
|
|
SERVER_NAME="{{ minecraft.server_name }}"
|
|
|
|
# Timestamp for this backup
|
|
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
|
|
|
|
# Log function
|
|
log() {
|
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
|
}
|
|
|
|
# Safety checks
|
|
if [ ! -d "$DATA_DIR" ]; then
|
|
log "ERROR: Data directory $DATA_DIR does not exist"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -d "$BACKUP_DIR" ]; then
|
|
log "Creating backup directory: $BACKUP_DIR"
|
|
mkdir -p "$BACKUP_DIR"
|
|
fi
|
|
|
|
# Create backup using tar
|
|
BACKUP_FILE="${BACKUP_DIR}/${SERVER_NAME}_${TIMESTAMP}"
|
|
|
|
log "Starting backup of $DATA_DIR..."
|
|
|
|
# Use atomic save if RCON is enabled
|
|
USE_RCON="{{ minecraft.rcon_enabled | default('false') }}"
|
|
if [ "$USE_RCON" = "true" ]; then
|
|
log "Using RCON for world save..."
|
|
# Send save-all command via rcon-cli or telnet
|
|
# This is a best-effort operation - fall back to tar if it fails
|
|
if command -v rcon-cli >/dev/null 2>&1; then
|
|
rcon-cli -a localhost -p {{ minecraft.rcon_port }} -P {{ minecraft.rcon_password }} save-all || true
|
|
sleep 5
|
|
elif command -v mcrcon >/dev/null 2>&1; then
|
|
mcrcon -H localhost -P {{ minecraft.rcon_port }} -p {{ minecraft.rcon_password }} "save-all" || true
|
|
sleep 5
|
|
else
|
|
log "RCON client not found, skipping save-all command (backup may be inconsistent)"
|
|
fi
|
|
fi
|
|
|
|
# Deterministic backup using tar
|
|
if [ "$COMPRESS" = "true" ]; then
|
|
BACKUP_FILE="${BACKUP_FILE}.tar.gz"
|
|
log "Creating compressed backup: $BACKUP_FILE"
|
|
tar --warning=no-file-changed -czf "$BACKUP_FILE" \
|
|
--exclude="$DATA_DIR/logs/*.gz" \
|
|
-C "$(dirname $DATA_DIR)" "$(basename $DATA_DIR)"
|
|
else
|
|
BACKUP_FILE="${BACKUP_FILE}.tar"
|
|
log "Creating uncompressed backup: $BACKUP_FILE"
|
|
tar --warning=no-file-changed -cf "$BACKUP_FILE" \
|
|
--exclude="$DATA_DIR/logs/*.gz" \
|
|
-C "$(dirname $DATA_DIR)" "$(basename $DATA_DIR)"
|
|
fi
|
|
|
|
log "Backup created successfully: $BACKUP_FILE"
|
|
|
|
# Prune old backups, retaining the most recent N
|
|
log "Pruning old backups (keeping $RETENTION_COUNT)..."
|
|
|
|
# Count backups (whether .tar or .tar.gz)
|
|
BACKUP_COUNT=$(ls "${BACKUP_DIR}/"${SERVER_NAME}_*.tar* 2>/dev/null | wc -l)
|
|
OVERFLOW=$((BACKUP_COUNT - RETENTION_COUNT))
|
|
|
|
if [ "$OVERFLOW" -gt 0 ]; then
|
|
# Remove oldest backups
|
|
ls -t "${BACKUP_DIR}/"${SERVER_NAME}_*.tar* 2>/dev/null | tail -n "$OVERFLOW" | while read -r old_backup; do
|
|
log "Removing old backup: $old_backup"
|
|
rm -f "$old_backup"
|
|
done
|
|
else
|
|
log "Backup count ($BACKUP_COUNT) is within retention limit ($RETENTION_COUNT)"
|
|
fi
|
|
|
|
log "Backup completed at $(date '+%Y-%m-%d %H:%M:%S')"
|
|
log "Current backups:"
|
|
ls -lh "${BACKUP_DIR}/" 2>/dev/null || true
|