Minecraft Field guide

How to back up a Minecraft server and restore its world

Back up Paper, copy it off the VPS and restore without deleting the current world: files, permissions and verification.

How to back up a Minecraft server and restore its world

A useful Minecraft backup must survive losing the VPS and contain enough information to bring the same server back. Make a consistent copy with Paper stopped, retrieve it over SSH, then inspect recovery in a separate directory before replacing anything.

This guide follows the Ubuntu Paper setup: the service is minecraft, its account is minecraft, and the server directory is /srv/minecraft. You need SSH, sudo, GNU tar and space for both an archive and an extracted server. Adapt paths before using this on a different installation. Bedrock, modpacks and proxy networks require their own data inventory.

Archive after saving and stopping. Preserve the current directory before recovery; back up an external database separately.

Decide what you need to recover#

Write down the oldest acceptable recovery point: if you take one copy each evening, you may lose everything players built after that copy. Before an update or a major configuration change, take an additional named recovery point.

Use the whole server directory for this single-server example. It keeps all local world data, player state, allowlists, configuration, plugin files and the exact Paper JAR together. Paper's update documentation identifies worlds, settings and plugin data as backup material. A seed or a map download alone cannot reconstruct the server.

Check for additional worlds, paths changed by plugins, symlinks and external database connections. Archiving a symlink does not copy its target. A database on another service is outside /srv/minecraft; use that database's own backup procedure and coordinate the recovery point with the game. Stop here if you cannot account for those dependencies.

Also save a private administration note with the Paper/game build, Java version, world name, backup time, expected landmarks and external restore requirements. Record the service definition separately: this archive does not include /etc/systemd/system or install Java on a replacement VPS.

Inspect the service and available storage#

Run these commands on the VPS:

bash
sudo systemctl cat minecraft
sudo systemctl show minecraft -p WorkingDirectory -p User -p ExecStart
sudo du -sh /srv/minecraft
df -h /srv /var/backups
sudo find /srv/minecraft -type l -ls
/usr/bin/java -version

Confirm the unit actually uses this directory. /usr/bin/java -version describes that executable; if ExecStart pins another Java path, run its -version instead. Confirm no other service, scheduler or manually launched Java process writes to the directory. Free space must cover the archive, a transfer copy and, when restoring, a staged extracted copy while the current directory is retained. Compression ratio varies; do not assume a large world will shrink enough to fit.

Archives can contain player information and database credentials. Keep their storage private; a publicly served directory is unsuitable. If you want to administer your own VPS through a browser, Nexardo's server management panel is an option with separate setup. This manual archive does not automatically enroll the service or provide panel restoration.

Stop Paper and confirm the save finished#

Announce maintenance, stop the service, then read the final saving messages:

bash
sudo systemctl stop minecraft
sudo journalctl -u minecraft -n 80 --no-pager
sudo systemctl show minecraft -p ActiveState -p MainPID -p Result

Continue only after saving completed normally, ActiveState=inactive, MainPID=0 and Result=success. A timeout, forced kill or disk error does not establish a clean recovery point. The systemd service manual explains that exceeding the stop timeout can terminate the process forcibly. Resolve the shutdown problem before treating a subsequent copy as known-good.

World save commands alone do not establish consistency for every plugin's local database. Planned downtime avoids copying a directory that Paper and plugins are still changing. Keep all writers stopped until the archive finishes.

Create the archive and stage a private download#

Run this as your ordinary SSH account with sudo. It requires the recorded JAR fingerprint from the setup guide to match; if it is missing, establish and record the current known build before proceeding.

bash
(
set -e
test "$(sudo systemctl show minecraft -p ActiveState --value)" = inactive
test "$(sudo systemctl show minecraft -p MainPID --value)" = 0
test "$(sudo systemctl show minecraft -p Result --value)" = success
sudo -u minecraft /bin/bash -c 'cd /srv/minecraft && sha256sum -c paper.jar.sha256'
sudo install -d -m 700 /var/backups/minecraft
MC_BACKUP="/var/backups/minecraft/backup-$(date +%F-%H%M%S).tar.gz"
sudo test ! -e "$MC_BACKUP"
sudo tar -C /srv -czf "$MC_BACKUP.partial" minecraft
sudo chmod 600 "$MC_BACKUP.partial"
sudo tar -tzf "$MC_BACKUP.partial" > /dev/null
sudo mv "$MC_BACKUP.partial" "$MC_BACKUP"
(
set -e
install -d -m 700 "$HOME/minecraft-transfer/restore"
chmod 700 "$HOME/minecraft-transfer"
chmod 700 "$HOME/minecraft-transfer/restore"
test ! -e "$HOME/minecraft-transfer/restore/backup.tar.gz"
test ! -e "$HOME/minecraft-transfer/restore/backup.tar.gz.sha256"
)
sudo install -o "$(id -un)" -m 600 "$MC_BACKUP" "$HOME/minecraft-transfer/backup.tar.gz"
cd "$HOME/minecraft-transfer"
umask 077
sha256sum backup.tar.gz > backup.tar.gz.sha256
)

The subshell stops on failure. A .partial file is unfinished work, not a retained recovery point. GNU tar's packaged upstream manual documents a nonzero result when files change during creation; investigate unexpected writers rather than accepting that archive. Listing the archive checks readability, not whether the world will play correctly.

Paper remains stopped. For a routine backup, you can start it after archive success and inspect the startup log. For a pre-update backup, keep it stopped and follow the update workflow.

On your computer, create a fresh dated private directory and download both files. Each transfer must succeed before checking the checksum; a failed transfer stops this block. Replace SSH_USER and VPS_IP; add -P YOUR_SSH_PORT to scp for a custom port:

bash
(
set -e
umask 077
MC_BACKUP_COPY="$(mktemp -d "$HOME/minecraft-backup-$(date +%F-%H%M%S).XXXXXX")"
chmod 700 "$MC_BACKUP_COPY"
cd "$MC_BACKUP_COPY"
scp SSH_USER@VPS_IP:minecraft-transfer/backup.tar.gz ./backup.tar.gz
scp SSH_USER@VPS_IP:minecraft-transfer/backup.tar.gz.sha256 ./backup.tar.gz.sha256
sha256sum -c backup.tar.gz.sha256
pwd
)

Keep both filenames unchanged in that dated directory: the checksum file refers to backup.tar.gz. Record the printed directory path and the administration note, then remove only the temporary transfer files from the VPS. If a transfer fails, the new directory contains an incomplete attempt; do not proceed to inspection or restoration. A checksum detects transfer differences; it does not authenticate an unknown archive. Keep at least one copy outside the VPS's storage and credentials. A second folder on the same disk cannot survive that disk's loss.

Inspect a copy without touching production#

Use only an archive you created and trust. On a separate Linux machine, enter the deliberately selected dated download directory containing the unchanged archive/checksum pair, then:

bash
(
set -e
sha256sum -c backup.tar.gz.sha256
MC_INSPECT_DIR="$(mktemp -d)"
chmod 700 "$MC_INSPECT_DIR"
tar --no-same-owner -xzf backup.tar.gz -C "$MC_INSPECT_DIR"
test -f "$MC_INSPECT_DIR/minecraft/server.properties"
test -f "$MC_INSPECT_DIR/minecraft/paper.jar"
cd "$MC_INSPECT_DIR/minecraft"
sha256sum -c paper.jar.sha256
ls -la
)

Compare the extracted contents with your inventory, including every configured world and plugin dependency. This establishes extraction and fingerprint checks. A restore drill goes further: use a Java-compatible isolated test server, prevent ordinary players from joining, keep external plugin database connections separated from production, then check the actual world, player inventories and important plugin operations in-game. Do not run two copies against the same live plugin database.

Restore while keeping the current directory#

Choose the dated recovery point deliberately: restoration discards changes made after it. On a replacement VPS, first recreate the Java installation, account and service from your administration notes and the setup guide; keep the service stopped. This example preserves an existing /srv/minecraft directory. Prepare private SSH staging on the destination VPS before uploading:

bash
(
set -e
install -d -m 700 "$HOME/minecraft-transfer/restore"
chmod 700 "$HOME/minecraft-transfer"
chmod 700 "$HOME/minecraft-transfer/restore"
test ! -e "$HOME/minecraft-transfer/restore/backup.tar.gz"
test ! -e "$HOME/minecraft-transfer/restore/backup.tar.gz.sha256"
)

The preparation checks reject leftover files from an earlier upload. If they fail, identify and move that previous pair to its own dated private directory before preparing again. From your computer, replace the directory placeholder with the selected verified recovery point and upload its unchanged pair. Stop here if either transfer fails:

bash
(
set -e
cd "/ABSOLUTE/PATH/TO/SELECTED_BACKUP_DIRECTORY"
sha256sum -c backup.tar.gz.sha256
scp backup.tar.gz SSH_USER@VPS_IP:minecraft-transfer/restore/backup.tar.gz
scp backup.tar.gz.sha256 SSH_USER@VPS_IP:minecraft-transfer/restore/backup.tar.gz.sha256
)

On the VPS, verify the unchanged pair in the restore upload directory:

bash
(
set -e
cd "$HOME/minecraft-transfer/restore"
sha256sum -c backup.tar.gz.sha256
)

Stop Paper and check clean saving as above. Confirm Java and the service settings match the archived build. Restore any external databases through their separately planned procedure while their writers are stopped. Then on the VPS:

bash
(
set -e
test "$(sudo systemctl show minecraft -p ActiveState --value)" = inactive
test "$(sudo systemctl show minecraft -p MainPID --value)" = 0
MC_RESTORE_STAGE="$(sudo mktemp -d /srv/minecraft-restore.XXXXXX)"
sudo tar --no-same-owner -xzf "$HOME/minecraft-transfer/restore/backup.tar.gz" -C "$MC_RESTORE_STAGE"
sudo test -f "$MC_RESTORE_STAGE/minecraft/paper.jar"
sudo test -f "$MC_RESTORE_STAGE/minecraft/server.properties"
sudo /bin/bash -c 'cd "$1/minecraft" && sha256sum -c paper.jar.sha256' _ "$MC_RESTORE_STAGE"
MC_PREVIOUS="/srv/minecraft.before-restore-$(date +%F-%H%M%S)"
sudo test ! -e "$MC_PREVIOUS"
sudo chown -R minecraft:minecraft "$MC_RESTORE_STAGE/minecraft"
sudo chmod 750 "$MC_RESTORE_STAGE/minecraft"
sudo mv /srv/minecraft "$MC_PREVIOUS"
sudo mv "$MC_RESTORE_STAGE/minecraft" /srv/minecraft
sudo systemctl start minecraft
sudo journalctl -u minecraft -n 100 --no-pager
)

Nothing is extracted over the current world. If moving or startup fails, stop and inspect; do not rerun the entire block blindly. The preserved directory still exists, and the inspected staged copy or restored directory can be identified by their paths. Returning to the previous state also requires stopping the service and restoring its matching external data.

Check the result and keep more than one recovery point#

Look for Done, then join with the matching client and verify the world name, known builds, player state and plugin features. A running process is insufficient.

ObservationNext check
A new empty world appearsConfirm the unit's working directory, level-name and extracted world inventory; stop before players build there
Permission errorsCheck directory ownership and readability as minecraft, including plugin paths
Java class-version errorCompare the archived Paper branch with the executable in ExecStart
Plugin data differs from the worldCheck whether the external database was restored to the coordinated point
Archive checks pass but the world failsPreserve logs and current copies; try an older recovery point on the isolated test server

Keep several dated copies and a separate pre-update baseline until the new version has been accepted. Set retention around the amount of progress you can lose and available storage; delete a known old copy only after another off-VPS copy and a restore drill are confirmed.