Update backup script
This commit is contained in:
273
scripts/backup.sh
Executable file
273
scripts/backup.sh
Executable file
@@ -0,0 +1,273 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# Adjustable parameters
|
||||
## The number of backups to keep
|
||||
BACKUPS_KEEP=24
|
||||
|
||||
# Global variables (Do NOT change)
|
||||
CLEANUP_FABRIC=false
|
||||
CLEANUP_LUCKPERMS=false
|
||||
CLEANUP_PAPER=false
|
||||
|
||||
main() {
|
||||
log_info "=== Backup run started at $(date -u '+%Y-%m-%dT%H:%M:%SZ') ==="
|
||||
log_info "Keeping last $BACKUPS_KEEP backups"
|
||||
|
||||
check_root_permissions
|
||||
init_backup
|
||||
|
||||
broadcast_status started
|
||||
|
||||
backup_bluemap || cleanup_failure
|
||||
backup_fabric || cleanup_failure
|
||||
backup_luckperms || cleanup_failure
|
||||
backup_paper || cleanup_failure
|
||||
backup_schematics || cleanup_failure
|
||||
backup_velocity || cleanup_failure
|
||||
|
||||
finalize_backup || cleanup_failure
|
||||
prune_backups
|
||||
cleanup_success
|
||||
|
||||
broadcast_status finished
|
||||
|
||||
log_info "=== Backup run completed successfully at $(date -u '+%Y-%m-%dT%H:%M:%SZ') ==="
|
||||
}
|
||||
|
||||
check_root_permissions() {
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
log_error "This script must be run by the root user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Running as root (EUID=$EUID)"
|
||||
}
|
||||
|
||||
init_backup() {
|
||||
BACKUP_ID="$(date -u +%Y-%m-%dT%H-%M-%SZ)"
|
||||
BACKUP_DIR="/backups/$BACKUP_ID"
|
||||
|
||||
log_info "Creating backup directory: $BACKUP_DIR"
|
||||
|
||||
docker compose run --rm init sh -c '
|
||||
mkdir -p "$1" &&
|
||||
chown minecraft_server:minecraft_server "$1"
|
||||
' -- "$BACKUP_DIR" || {
|
||||
log_error "Failed to create backup directory $BACKUP_DIR"
|
||||
exit 1
|
||||
}
|
||||
|
||||
log_info "Backup directory created and ownership set"
|
||||
}
|
||||
|
||||
broadcast_status() {
|
||||
STATUS="$1"
|
||||
log_info "Broadcasting backup status: $STATUS"
|
||||
|
||||
if docker compose exec -T fabric true > /dev/null 2>&1; then
|
||||
docker compose exec -T fabric rcon-cli "tellraw @a [{\"text\":\"Server\",\"color\":\"light_purple\"},{\"text\":\": Backup $STATUS.\",\"color\":\"white\"}]" > /dev/null 2>&1
|
||||
fi
|
||||
if docker compose exec -T paper true > /dev/null 2>&1; then
|
||||
docker compose exec -T paper rcon-cli "tellraw @a [{\"text\":\"Server\",\"color\":\"light_purple\"},{\"text\":\": Backup $STATUS.\",\"color\":\"white\"}]" > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
backup_bluemap() {
|
||||
log_info "Starting Bluemap backup..."
|
||||
|
||||
docker compose run --rm init sh -c '
|
||||
cp -a --reflink=auto /bluemap_data "$1" &&
|
||||
cp -a --reflink=auto /bluemap_maps "$1" &&
|
||||
cp -a --reflink=auto /bluemap_web "$1"
|
||||
' -- "$BACKUP_DIR" || return 1
|
||||
|
||||
log_info "Finished Bluemap backup"
|
||||
}
|
||||
|
||||
backup_fabric() {
|
||||
log_info "Starting Fabric backup..."
|
||||
|
||||
CLEANUP_FABRIC=false
|
||||
if docker compose exec -T fabric true > /dev/null 2>&1; then
|
||||
CLEANUP_FABRIC=true
|
||||
log_info "Fabric server detected, disabling saves"
|
||||
|
||||
docker compose exec -T fabric rcon-cli save-off > /dev/null 2>&1
|
||||
docker compose exec -T fabric rcon-cli save-all > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
docker compose run --rm init sh -c '
|
||||
mkdir -p "$1"/fabric_data &&
|
||||
cp -a --reflink=auto /fabric_data/g4mespeed "$1"/fabric_data &&
|
||||
cp -a --reflink=auto /fabric_data/fabric-essentials.json "$1"/fabric_data &&
|
||||
cp -a --reflink=auto /fabric_data/world "$1"/fabric_data
|
||||
' -- "$BACKUP_DIR" || return 1
|
||||
|
||||
log_info "Finished Fabric backup"
|
||||
}
|
||||
|
||||
backup_luckperms() {
|
||||
log_info "Starting LuckPerms backup..."
|
||||
|
||||
CLEANUP_LUCKPERMS=false
|
||||
if ! docker compose exec -T luckperms true > /dev/null 2>&1; then
|
||||
CLEANUP_LUCKPERMS=true
|
||||
log_info "LuckPerms not running, starting temporary container"
|
||||
|
||||
docker compose up --wait luckperms > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
docker compose exec luckperms sh -c '
|
||||
pg_dump -U luckperms luckperms > "$1"/luckperms.sql
|
||||
' -- "$BACKUP_DIR" || return 1
|
||||
|
||||
log_info "Finished LuckPerms backup"
|
||||
}
|
||||
|
||||
backup_paper() {
|
||||
log_info "Starting Paper backup..."
|
||||
|
||||
CLEANUP_PAPER=false
|
||||
if docker compose exec -T paper true > /dev/null 2>&1; then
|
||||
CLEANUP_PAPER=true
|
||||
log_info "Paper server detected, disabling saves"
|
||||
|
||||
docker compose exec -T paper rcon-cli save-off > /dev/null 2>&1
|
||||
docker compose exec -T paper rcon-cli save-all > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
docker compose run --rm init sh -c '
|
||||
mkdir -p "$1"/paper_data/plugins &&
|
||||
cp -a --reflink=auto /paper_data/plugins/Multiverse-Inventories "$1"/paper_data/plugins &&
|
||||
cp -a --reflink=auto /paper_data/plugins/Essentials "$1"/paper_data/plugins &&
|
||||
cp -a --reflink=auto /paper_data/plugins/PlotSquared "$1"/paper_data/plugins &&
|
||||
cp -a --reflink=auto /paper_data/creative "$1"/paper_data &&
|
||||
cp -a --reflink=auto /paper_data/creative_nether "$1"/paper_data &&
|
||||
cp -a --reflink=auto /paper_data/survival "$1"/paper_data &&
|
||||
cp -a --reflink=auto /paper_data/survival_nether "$1"/paper_data &&
|
||||
cp -a --reflink=auto /paper_data/survival_the_end "$1"/paper_data
|
||||
' -- "$BACKUP_DIR" || return 1
|
||||
|
||||
log_info "Finished Paper backup"
|
||||
}
|
||||
|
||||
backup_schematics() {
|
||||
log_info "Starting schematics backup..."
|
||||
|
||||
docker compose run --rm init sh -c '
|
||||
cp -a --reflink=auto /schematics "$1"
|
||||
' -- "$BACKUP_DIR" || return 1
|
||||
|
||||
log_info "Finished schematics backup"
|
||||
}
|
||||
|
||||
backup_velocity() {
|
||||
log_info "Starting Velocity backup..."
|
||||
|
||||
docker compose run --rm init sh -c 'mkdir -p "$1"/velocity_data/plugins/dclink-velocity' -- "$BACKUP_DIR" || return 1
|
||||
|
||||
docker compose run --rm sqlite_helper \
|
||||
/velocity_data/plugins/dclink-velocity/dclink.db \
|
||||
".backup $BACKUP_DIR/velocity_data/plugins/dclink-velocity/dclink.db" || return 1
|
||||
|
||||
log_info "Finished Velocity backup"
|
||||
}
|
||||
|
||||
finalize_backup() {
|
||||
log_info "Finalizing backup $BACKUP_ID"
|
||||
|
||||
docker compose run --rm init chown -R minecraft_server:minecraft_server "$BACKUP_DIR" || {
|
||||
log_error "Failed to update ownership of backup files"
|
||||
return 1
|
||||
}
|
||||
|
||||
log_info "Backup ownership updated"
|
||||
|
||||
docker compose run --rm init sh -c '
|
||||
ln -sfn "$1" /backups/.latest_tmp &&
|
||||
mv -Tf /backups/.latest_tmp /backups/latest
|
||||
' -- "$BACKUP_DIR" || {
|
||||
log_error "Failed to update /backups/latest symlink"
|
||||
return 1
|
||||
}
|
||||
|
||||
log_info "Updated /backups/latest symlink"
|
||||
}
|
||||
|
||||
prune_backups() {
|
||||
BACKUPS_ALL=$(docker compose run --rm init sh -c '
|
||||
find /backups -mindepth 1 -maxdepth 1 -type d \
|
||||
-name "????-??-??T??-??-??Z" | sort
|
||||
')
|
||||
|
||||
BACKUPS_TOTAL=$(echo "$BACKUPS_ALL" | wc -l)
|
||||
BACKUPS_PRUNE=$((BACKUPS_TOTAL - BACKUPS_KEEP))
|
||||
|
||||
[ "$BACKUPS_PRUNE" -le 0 ] && BACKUPS_PRUNE=0
|
||||
|
||||
log_info "Total backups found: $BACKUPS_TOTAL"
|
||||
log_info "Backups to prune: $BACKUPS_PRUNE"
|
||||
|
||||
echo "$BACKUPS_ALL" | head -n "$BACKUPS_PRUNE" | while read -r OLD_BACKUP_DIR; do
|
||||
log_info "Removing old backup: $OLD_BACKUP_DIR"
|
||||
docker compose run --rm init rm -rf "$OLD_BACKUP_DIR"
|
||||
done
|
||||
}
|
||||
|
||||
trap cleanup_trap HUP INT QUIT ABRT TERM
|
||||
cleanup_trap() {
|
||||
log_info "Backup cancelled by signal"
|
||||
broadcast_status cancelled
|
||||
cleanup
|
||||
delete_backup
|
||||
exit 0
|
||||
}
|
||||
|
||||
cleanup_failure() {
|
||||
log_info "Running cleanup due to failure"
|
||||
broadcast_status failed
|
||||
cleanup
|
||||
delete_backup
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup_success() {
|
||||
log_info "Running cleanup"
|
||||
cleanup
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
# Ignore specified conditions to ensure cleanup runs to completion uninterrupted
|
||||
trap '' HUP INT QUIT ABRT TERM
|
||||
|
||||
if [ "$CLEANUP_FABRIC" = true ]; then
|
||||
log_info "Re-enabling Fabric saves"
|
||||
docker compose exec -T fabric rcon-cli save-on > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
if [ "$CLEANUP_LUCKPERMS" = true ]; then
|
||||
log_info "Bringing down temporary LuckPerms container"
|
||||
docker compose stop luckperms > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
if [ "$CLEANUP_PAPER" = true ]; then
|
||||
log_info "Re-enabling Paper saves"
|
||||
docker compose exec -T paper rcon-cli save-on > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
delete_backup() {
|
||||
if ! docker compose run --rm init rm -rf "$BACKUP_DIR"; then
|
||||
log_error "Failed to remove $BACKUP_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
log_info() {
|
||||
echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] INFO: $*"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] ERROR: $*" >&2
|
||||
}
|
||||
|
||||
main
|
||||
Reference in New Issue
Block a user