minecraft-server/scripts/backup.sh
s8n-ru 0dad38e02e Initial commit: racked.ru Minecraft server config snapshot
Captures live config state of nullstone Purpur 1.21.11 server:
- docker-compose.yml (itzg/minecraft-server image, MODRINTH_PROJECTS + PLUGINS lists)
- All plugin configs under live-server/plugins/ (no DBs, no jars, no world data)
- Server core: bukkit.yml, spigot.yml, purpur.yml, paper-global.yml, paper-world-defaults.yml, server.properties

Excluded via .gitignore:
- World data (world/, world_nether/, world_the_end/, auth_limbo/)
- Sensitive: AuthMe DB (password hashes), Lands DB, CoreProtect DB, Essentials userdata
- Jars (auto-fetched), logs, caches, .paper-remapped
2026-04-30 18:33:38 +01:00

130 lines
6.3 KiB
Bash
Executable file

#!/usr/bin/env bash
# /opt/docker/backup.sh
# Backs up all Docker service databases and named volumes to /opt/backups/
# Run as root via cron. Keeps 7 daily backups.
set -euo pipefail
BACKUP_DIR="/opt/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="${BACKUP_DIR}/${TIMESTAMP}"
LOG="${BACKUP_DIR}/backup.log"
KEEP_DAYS=7
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"; }
mkdir -p "$BACKUP_PATH"
log "=== Backup started: ${TIMESTAMP} ==="
# ── Matrix PostgreSQL ──────────────────────────────────────────────
log "Dumping Matrix PostgreSQL..."
if docker ps --format '{{.Names}}' | grep -q '^matrix-postgres$'; then
docker exec matrix-postgres pg_dump -U synapse synapse \
| gzip > "${BACKUP_PATH}/matrix-postgres-${TIMESTAMP}.sql.gz" \
&& log " Matrix Postgres: OK ($(du -sh "${BACKUP_PATH}/matrix-postgres-${TIMESTAMP}.sql.gz" | cut -f1))" \
|| log " Matrix Postgres: FAILED"
else
log " matrix-postgres not running — skipping"
fi
# ── Rocket.Chat MongoDB ────────────────────────────────────────────
log "Dumping Rocket.Chat MongoDB..."
if docker ps --format '{{.Names}}' | grep -q '^mongodb$'; then
docker exec mongodb mongodump \
-u admin -p CHANGE_ME_MONGO_ADMIN_PASSWORD \
--authenticationDatabase admin \
--db rocketchat \
--archive \
| gzip > "${BACKUP_PATH}/rocketchat-mongo-${TIMESTAMP}.archive.gz" \
&& log " MongoDB: OK ($(du -sh "${BACKUP_PATH}/rocketchat-mongo-${TIMESTAMP}.archive.gz" | cut -f1))" \
|| log " MongoDB: FAILED"
else
log " mongodb not running — skipping"
fi
# ── Named Docker volumes ───────────────────────────────────────────
log "Backing up Docker volumes..."
for VOLUME in synapse-media rocketchat-uploads; do
if docker volume ls --format '{{.Name}}' | grep -q "^matrix_${VOLUME}\|^rocketchat_${VOLUME}\|^${VOLUME}$"; then
ACTUAL_VOL=$(docker volume ls --format '{{.Name}}' | grep "${VOLUME}" | head -1)
docker run --rm \
-v "${ACTUAL_VOL}:/volume:ro" \
-v "${BACKUP_PATH}:/backup" \
alpine \
tar czf "/backup/${VOLUME}-${TIMESTAMP}.tar.gz" -C /volume . \
&& log " Volume ${VOLUME}: OK" \
|| log " Volume ${VOLUME}: FAILED"
else
log " Volume ${VOLUME}: not found — skipping"
fi
done
# ── Config files (bind mounts) ─────────────────────────────────────
log "Backing up config directories..."
tar czf "${BACKUP_PATH}/configs-${TIMESTAMP}.tar.gz" \
/opt/docker/traefik/traefik.yml \
/opt/docker/traefik/config/ \
/opt/docker/matrix/docker-compose.yml \
/opt/docker/matrix/element-config/ \
/opt/docker/matrix/synapse-config/homeserver.yaml \
/opt/docker/matrix/synapse-config/matrix.example.com.log.config \
/opt/docker/rocketchat/docker-compose.yml \
2>/dev/null && log " Configs: OK" || log " Configs: partial (some files missing)"
# IMPORTANT: signing key is sensitive — back up separately with tight perms
if [ -f /opt/docker/matrix/synapse-config/matrix.example.com.signing.key ]; then
cp /opt/docker/matrix/synapse-config/matrix.example.com.signing.key \
"${BACKUP_PATH}/synapse-signing-key-${TIMESTAMP}.key"
chmod 600 "${BACKUP_PATH}/synapse-signing-key-${TIMESTAMP}.key"
log " Synapse signing key: backed up (600)"
fi
# ── Minecraft server ───────────────────────────────────────────────
log "Backing up Minecraft server..."
if docker ps --format '{{.Names}}' | grep -q '^minecraft-mc$'; then
# Server is running - create consistent world snapshot
docker exec minecraft-mc bash -c \
"cd /data && tar czf /tmp/mc-world-backup-${TIMESTAMP}.tar.gz world/ world_nether/ world_the_end/ 2>/dev/null" && \
docker cp minecraft-mc:/tmp/mc-world-backup-${TIMESTAMP}.tar.gz "${BACKUP_PATH}/" && \
docker exec minecraft-mc rm -f /tmp/mc-world-backup-${TIMESTAMP}.tar.gz && \
log " Minecraft world: OK ($(du -sh "${BACKUP_PATH}/mc-world-backup-${TIMESTAMP}.tar.gz" | cut -f1))" \
|| log " Minecraft world: FAILED"
# Backup configs and plugins
tar czf "${BACKUP_PATH}/minecraft-configs-${TIMESTAMP}.tar.gz" \
/opt/docker/minecraft/server.properties \
/opt/docker/minecraft/purpur.yml \
/opt/docker/minecraft/spigot.yml \
/opt/docker/minecraft/paper-*.yml \
/opt/docker/minecraft/bukkit.yml \
/opt/docker/minecraft/ops.json \
/opt/docker/minecraft/banned-*.json \
/opt/docker/minecraft/eula.txt \
2>/dev/null && \
log " Minecraft configs: OK" \
|| log " Minecraft configs: partial (expected)"
else
# Server is stopped - backup everything directly
tar czf "${BACKUP_PATH}/minecraft-full-backup-${TIMESTAMP}.tar.gz" \
/opt/docker/minecraft/world/ \
/opt/docker/minecraft/world_nether/ \
/opt/docker/minecraft/world_the_end/ \
/opt/docker/minecraft/plugins/ \
/opt/docker/minecraft/server.properties \
/opt/docker/minecraft/purpur.yml \
/opt/docker/minecraft/spigot.yml \
2>/dev/null && \
log " Minecraft (full, offline): OK ($(du -sh "${BACKUP_PATH}/minecraft-full-backup-${TIMESTAMP}.tar.gz" | cut -f1))" \
|| log " Minecraft (offline): partial"
fi
"${BACKUP_PATH}/synapse-signing-key-${TIMESTAMP}.key"
chmod 600 "${BACKUP_PATH}/synapse-signing-key-${TIMESTAMP}.key"
log " Synapse signing key: backed up (600)"
fi
# ── Prune old backups ──────────────────────────────────────────────
log "Pruning backups older than ${KEEP_DAYS} days..."
find "$BACKUP_DIR" -maxdepth 1 -type d -mtime "+${KEEP_DAYS}" -exec rm -rf {} + 2>/dev/null || true
find "$BACKUP_DIR" -maxdepth 1 -name "*.log" -mtime +30 -delete 2>/dev/null || true
BACKUP_SIZE=$(du -sh "$BACKUP_PATH" | cut -f1)
log "=== Backup complete: ${BACKUP_PATH} (${BACKUP_SIZE}) ==="