Update backup script

This commit is contained in:
2026-01-21 19:14:59 +00:00
parent 0024c5e1d7
commit 6b3eec8303
10 changed files with 383 additions and 80 deletions

273
scripts/backup.sh Executable file
View 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

45
scripts/init.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/usr/bin/env sh
# Define a helper function that runs a command
# If the command fails, the script prints an error message
# and exits immediately.
run() {
# "$@" expands to all arguments passed to this function
# and preserves proper word splitting and quoting.
"$@" || {
echo "Error: command failed: $*" >&2
exit 1
}
}
# Make sure volumes have correct permissions
run chown minecraft_server:minecraft_server /bluemap_data
run chown minecraft_server:minecraft_server /bluemap_web
run chown minecraft_server:minecraft_server /bluemap_maps
run chown minecraft_server:minecraft_server /fabric_data
run chown minecraft_server:minecraft_server /luckperms_data
run chown minecraft_server:minecraft_server /paper_data
run chown minecraft_server:minecraft_server /schematics
run chown minecraft_server:minecraft_server /velocity_data
# Make sure nested volume mount points exist
run mkdir -p /fabric_data/bluemap/web/maps
run chown minecraft_server:minecraft_server /fabric_data/bluemap
run chown minecraft_server:minecraft_server /fabric_data/bluemap/web
run chown minecraft_server:minecraft_server /fabric_data/bluemap/web/maps
run mkdir -p /fabric_data/config/worldedit/schematics
run chown minecraft_server:minecraft_server /fabric_data/config
run chown minecraft_server:minecraft_server /fabric_data/config/worldedit
run chown minecraft_server:minecraft_server /fabric_data/config/worldedit/schematics
run mkdir -p /paper_data/bluemap/web/maps
run chown minecraft_server:minecraft_server /paper_data/bluemap
run chown minecraft_server:minecraft_server /paper_data/bluemap/web
run chown minecraft_server:minecraft_server /paper_data/bluemap/web/maps
run mkdir -p /paper_data/plugins/WorldEdit/schematics
run chown minecraft_server:minecraft_server /paper_data/plugins
run chown minecraft_server:minecraft_server /paper_data/plugins/WorldEdit
run chown minecraft_server:minecraft_server /paper_data/plugins/WorldEdit/schematics

View File

@@ -1,30 +0,0 @@
#!/usr/bin/env sh
# Define a helper function that runs a command
# If the command fails, the script prints an error message
# and exits immediately.
run() {
# "$@" expands to all arguments passed to this function
# and preserves proper word splitting and quoting.
"$@" || {
echo "Error: command failed: $*" >&2
exit 1
}
}
# Ensure required directories exist
run mkdir -p /fabric_data/bluemap/web/maps
run mkdir -p /fabric_data/config/worldedit/schematics
run mkdir -p /paper_data/bluemap/web/maps
run mkdir -p /paper_data/plugins/WorldEdit/schematics
# Ensure correct permissions of docker volumes
run chown -R minecraft_server:minecraft_server /bluemap_data
run chown -R minecraft_server:minecraft_server /bluemap_web
run chown -R minecraft_server:minecraft_server /bluemap_maps
run chown -R minecraft_server:minecraft_server /fabric_data
run chown -R minecraft_server:minecraft_server /luckperms_data
run chown -R minecraft_server:minecraft_server /paper_data
run chown -R minecraft_server:minecraft_server /schematics
run chown -R minecraft_server:minecraft_server /velocity_data

View File

@@ -43,7 +43,7 @@ docker exec illegal_crime_paper rcon-cli save-all
PG_USER="luckperms"
PG_DB="luckperms"
BACKUP_DIR_CONTAINER="/backups/$DATE"
docker exec illegal_crime_luckperms_db pg_dump -U "$PG_USER" -F c -b -v -f "$BACKUP_DIR_CONTAINER/luckperms.sql" "$PG_DB"
docker exec illegal_crime_luckperms pg_dump -U "$PG_USER" -F c -b -v -f "$BACKUP_DIR_CONTAINER/luckperms.sql" "$PG_DB"
# Copy WorldEdit schematics
cp -r "$PROJECT_DIR/schematics" "$BACKUP_DIR_HOST/"
@@ -109,4 +109,3 @@ fi
# Update permissions of backed up files
chown -R "$PUID:$PGID" "$BACKUPS_DIR_HOST"
chmod -R 770 "$BACKUPS_DIR_HOST"