Reliable Minecraft hosting requires more than occasional snapshots. You need measurable recovery targets, automated backup schedules, and tested restore workflows. Community surveys consistently show that data loss is the number one reason players leave a server permanently. A server that loses 4 hours of player builds due to a failed restore or missing backup loses those players forever. The investment in a proper backup strategy is trivial compared to the community trust it protects.
---
Define Recovery Objectives
Before configuring backups, set explicit targets for how much data you can afford to lose (RPO) and how quickly you must recover (RTO):
RPO (Recovery Point Objective)
The maximum acceptable amount of data loss measured in time. If your RPO is 15 minutes, your backup frequency must be at least every 15 minutes.
RTO (Recovery Time Objective)
The maximum acceptable downtime before the server is back online with recovered data.
| Server Type | RPO Target | RTO Target | Rationale |
| Small survival (< 10 players) | 30-60 minutes | 1 hour | Limited builds, lower risk |
| Active community (10-30 players) | 15 minutes | 30 minutes | Regular building activity |
| Large SMP (30-50+ players) | 5-15 minutes | 15 minutes | Significant hourly progress |
| Event / competition server | 1-5 minutes | 5 minutes | Every minute of play has value |
| Creative / build server | 30 minutes | 1 hour | Slower pace of changes |
Tiered Backup Schedule
Use multiple backup tiers for different recovery scenarios:
Tier 1: Frequent Incremental Backups
#!/bin/bash
<h1>/opt/scripts/backup-incremental.sh</h1>
<h1>Run via cron: */15 * * * * /opt/scripts/backup-incremental.sh</h1>
set -euo pipefail
MC_DIR="/opt/minecraft"
BACKUP_DIR="/backups/minecraft/incremental"
SCREEN_NAME="minecraft"
TIMESTAMP=$(date +%Y%m%d_%H%M)
<h1>Pause world saving</h1>
screen -S "$SCREEN_NAME" -p 0 -X stuff "save-off$(printf '\r')"
screen -S "$SCREEN_NAME" -p 0 -X stuff "save-all flush$(printf '\r')"
sleep 5
<h1>Incremental backup (only changed files since last full)</h1>
rsync -a --delete \
--link-dest="$BACKUP_DIR/latest" \
"$MC_DIR/world/" \
"$MC_DIR/world_nether/" \
"$MC_DIR/world_the_end/" \
"$BACKUP_DIR/$TIMESTAMP/"
<h1>Update latest symlink</h1>
ln -sfn "$BACKUP_DIR/$TIMESTAMP" "$BACKUP_DIR/latest"
<h1>Resume world saving</h1>
screen -S "$SCREEN_NAME" -p 0 -X stuff "save-on$(printf '\r')"
echo "[$(date)] Incremental backup completed: $TIMESTAMP"
Tier 2: Daily Full Backups
#!/bin/bash
<h1>/opt/scripts/backup-full.sh</h1>
<h1>Run via cron: 0 4 * * * /opt/scripts/backup-full.sh</h1>
set -euo pipefail
MC_DIR="/opt/minecraft"
BACKUP_DIR="/backups/minecraft/full"
SCREEN_NAME="minecraft"
TIMESTAMP=$(date +%Y%m%d)
<h1>Pause and save</h1>
screen -S "$SCREEN_NAME" -p 0 -X stuff "save-off$(printf '\r')"
screen -S "$SCREEN_NAME" -p 0 -X stuff "save-all flush$(printf '\r')"
sleep 10
<h1>Full compressed backup of entire server</h1>
tar -czf "$BACKUP_DIR/full_$TIMESTAMP.tar.gz" \
-C "$MC_DIR" \
world/ world_nether/ world_the_end/ \
plugins/ \
server.properties banned-ips.json banned-players.json \
ops.json whitelist.json \
bukkit.yml spigot.yml paper-global.yml paper-world-defaults.yml
<h1>Resume saving</h1>
screen -S "$SCREEN_NAME" -p 0 -X stuff "save-on$(printf '\r')"
<h1>Calculate and log backup size</h1>
SIZE=$(du -sh "$BACKUP_DIR/full_$TIMESTAMP.tar.gz" | cut -f1)
echo "[$(date)] Full backup completed: $SIZE"
Tier 3: Weekly Off-Site Archive
#!/bin/bash
<h1>/opt/scripts/backup-offsite.sh</h1>
<h1>Run via cron: 0 5 * * 0 /opt/scripts/backup-offsite.sh</h1>
BACKUP_DIR="/backups/minecraft/full"
REMOTE="b2:minecraft-backups"
<h1>Upload to Backblaze B2 (or S3, R2, etc.)</h1>
rclone sync "$BACKUP_DIR" "$REMOTE/full/" \
--transfers 4 \
--checkers 8 \
--max-age 8d
<h1>Verify upload</h1>
rclone check "$BACKUP_DIR" "$REMOTE/full/" --max-age 8d
echo "[$(date)] Off-site backup sync completed"
Tier 4: Monthly Archive with Retention
<h1>Retention policy</h1>
<h1>Incremental: keep 7 days</h1>
find /backups/minecraft/incremental/ -maxdepth 1 -mtime +7 -exec rm -rf {} \;
<h1>Full daily: keep 30 days</h1>
find /backups/minecraft/full/ -name "full_*.tar.gz" -mtime +30 -delete
<h1>Off-site: keep 90 days</h1>
rclone delete "$REMOTE/full/" --min-age 90d
<h1>Monthly archive: keep 1 year</h1>
<h1>Copy first-of-month full backup to archive tier</h1>
MONTH_BACKUP=$(ls -1 /backups/minecraft/full/full_$(date +%Y%m)01*.tar.gz 2>/dev/null | head -1)
if [ -n "$MONTH_BACKUP" ]; then
rclone copy "$MONTH_BACKUP" "$REMOTE/archive/"
fi
Backup Scope Checklist
Ensure every backup captures all necessary data:
| Data | Location | Criticality | Notes |
| Overworld | `world/` | Critical | Player builds, redstone, entities |
| Nether | `world_nether/` | Critical | Nether builds and portals |
| The End | `world_the_end/` | High | End builds, dragon state |
| Plugins | `plugins/` | High | Config files + plugin data |
| Server config | `server.properties` | High | Core server settings |
| Access lists | `ops.json`, `whitelist.json` | Medium | Permissions |
| Ban lists | `banned-*.json` | Medium | Moderation state |
| Paper/Spigot config | `*.yml` | Medium | Performance tuning |
| Server JAR + version | `server.jar` | Low | Re-downloadable |
Restore Drills
Backups without tested restores are a false sense of security. Run restore drills monthly.
Monthly Drill Procedure
#!/bin/bash
<h1>restore-drill.sh - Run on test/staging server</h1>
BACKUP_FILE="/backups/minecraft/full/full_$(date +%Y%m%d -d "yesterday").tar.gz"
TEST_DIR="/opt/minecraft-test"
DRILL_LOG="/var/log/restore-drills.log"
echo "=== RESTORE DRILL $(date) ===" >> "$DRILL_LOG"
START=$(date +%s)
<h1>1. Clean test environment</h1>
rm -rf "$TEST_DIR"
mkdir -p "$TEST_DIR"
<h1>2. Extract backup</h1>
tar -xzf "$BACKUP_FILE" -C "$TEST_DIR"
<h1>3. Start test server</h1>
cd "$TEST_DIR"
timeout 120 java -Xms2G -Xmx2G -jar server.jar --nogui &
SERVER_PID=$!
<h1>4. Wait for startup</h1>
for i in $(seq 1 60); do
if grep -q "Done" "$TEST_DIR/logs/latest.log" 2>/dev/null; then
break
fi
sleep 2
done
<h1>5. Validation checks</h1>
CHECKS_PASSED=0
CHECKS_TOTAL=4
<h1>Check: server started</h1>
if grep -q "Done" "$TEST_DIR/logs/latest.log"; then
echo "PASS: Server started successfully" >> "$DRILL_LOG"
((CHECKS_PASSED++))
else
echo "FAIL: Server did not start" >> "$DRILL_LOG"
fi
<h1>Check: world data present</h1>
if [ -d "$TEST_DIR/world/region" ]; then
REGIONS=$(ls "$TEST_DIR/world/region/" | wc -l)
echo "PASS: World data present ($REGIONS regions)" >> "$DRILL_LOG"
((CHECKS_PASSED++))
else
echo "FAIL: World data missing" >> "$DRILL_LOG"
fi
<h1>Check: plugins loaded</h1>
if [ -d "$TEST_DIR/plugins" ]; then
PLUGINS=$(ls "$TEST_DIR/plugins/"*.jar 2>/dev/null | wc -l)
echo "PASS: Plugins present ($PLUGINS jars)" >> "$DRILL_LOG"
((CHECKS_PASSED++))
else
echo "FAIL: Plugins missing" >> "$DRILL_LOG"
fi
<h1>Check: config intact</h1>
if [ -f "$TEST_DIR/server.properties" ]; then
echo "PASS: Config files intact" >> "$DRILL_LOG"
((CHECKS_PASSED++))
else
echo "FAIL: Config files missing" >> "$DRILL_LOG"
fi
<h1>6. Calculate time</h1>
END=$(date +%s)
DURATION=$((END - START))
<h1>7. Cleanup</h1>
kill $SERVER_PID 2>/dev/null
wait $SERVER_PID 2>/dev/null
<h1>8. Report</h1>
echo "Restore time: ${DURATION}s | Checks: $CHECKS_PASSED/$CHECKS_TOTAL" >> "$DRILL_LOG"
echo "=== END DRILL ===" >> "$DRILL_LOG"
Drill Results Template
| Drill Date | Backup Used | Restore Time | Checks Passed | Issues Found |
| 2026-04-01 | full_20260331.tar.gz | 47s | 4/4 | None |
| 2026-03-01 | full_20260228.tar.gz | 52s | 3/4 | Plugin config mismatch |
| 2026-02-01 | full_20260131.tar.gz | 45s | 4/4 | None |
Safe Plugin and Mod Rollback
Plugin and mod updates are the most common cause of server issues. Follow this safety process:
Pre-Update Checklist
<h1>1. Snapshot before update</h1>
cp -r plugins/ plugins_backup_$(date +%Y%m%d_%H%M)/
<h1>2. Document current versions</h1>
for jar in plugins/*.jar; do
echo "$jar: $(unzip -p "$jar" plugin.yml 2>/dev/null
grep "version:"
head -1)"
done > plugin_versions_$(date +%Y%m%d).txt
<h1>3. Test on staging first</h1>
cp new-plugin.jar /opt/minecraft-test/plugins/
<h1>Start test server and validate</h1>
<h1>4. Update production with rollback ready</h1>
screen -S minecraft -p 0 -X stuff "stop$(printf '\r')"
sleep 15
cp new-plugin.jar plugins/
<h1>Start server</h1>
Rollback Procedure
<h1>If the update causes issues:</h1>
screen -S minecraft -p 0 -X stuff "stop$(printf '\r')"
sleep 15
<h1>Restore previous plugin version</h1>
cp plugins_backup_YYYYMMDD_HHMM/problem-plugin.jar plugins/
<h1>Restart server</h1>
screen -S minecraft -p 0 -X stuff "./start.sh$(printf '\r')"
<h1>Verify rollback</h1>
tail -f logs/latest.log
grep -i "error\ warn\
plugin"
Compare plans on the Pricing page. Review platform options on the Services Overview page. Get migration help via Contact.
Frequently Asked Questions
How much storage do Minecraft backups need?
A typical survival server world with 20 regular players generates 2-10 GB of world data after a few months. Full compressed backups are 30-50% of raw size. Plan for 50-100 GB of backup storage to maintain 30 days of daily backups plus incremental snapshots. Off-site archival storage (Backblaze B2, S3) costs approximately $0.005-0.01/GB/month.
Should I back up the server JAR file?
The server JAR is re-downloadable, so it is low priority for backups. However, including it ensures you can restore to the exact same server version. This matters when plugin compatibility is tied to specific server builds. Include it in full backups but not in incremental ones.
Can I take backups while players are online?
Yes, but use `save-off` before the backup and `save-on` after. This prevents the server from writing to world files during the copy, which could create corrupt chunks. The `save-all flush` command ensures all pending changes are written to disk before the backup begins.
How do I verify a backup is not corrupted?
After creating a compressed backup, verify with `tar -tzf backup.tar.gz > /dev/null` (checks archive integrity). For deeper validation, restore to a test environment and verify the world loads correctly, region files are intact, and plugin data is accessible. Automated restore drills catch corruption that simple checksum verification misses.
What is the best off-site backup provider for game servers?
Backblaze B2 offers the best price-to-reliability ratio at $0.005/GB/month with free egress up to 3x stored data. AWS S3 Glacier is cheaper for archival but has slow retrieval times. Cloudflare R2 has no egress fees, making it cost-effective if you need frequent restores. Use rclone to sync backups to any provider with a single configuration.
Final Takeaway
A Minecraft backup strategy is only as good as its last successful restore drill. Set RPO and RTO targets based on your community size, automate the backup schedule across multiple tiers, sync to off-site storage, and run monthly restore drills. Document plugin versions before every update and keep rollback procedures ready. Your players' trust depends on your ability to recover quickly and completely.