This guide exists to help you stop losing the parts of your Minecraft server that are not blocks. Most owners back up the world, feel safe, and then discover after a crash that every player rank is gone, the shop balances reset, and CoreProtect has no history to roll back. The world is the biggest file in the folder, not the whole server.
It is written for owners, administrators, and the staff who inherit a server and a backup drive with one lonely world folder on it. The numbers below come from a lab run on Paper 26.2 with Ubuntu 26.04, a 16 vCPU host, and local NVMe. We measured what a full backup set actually contains, timed restic, and compared copying a live SQLite database against the supported backup methods. Then we restored a file and checked its hash.
The World Is Not the Server
Our test server’s backup set came to 260,767,199 bytes, about 249 MiB. Of that, the world was 247,428,400 bytes. Everything else, meaning every plugin configuration, the permission data, the economy database, the whitelist, the server properties, and the startup scripts, fit in roughly 13 MB. That is five percent of the set and about ninety-five percent of the pain when it goes missing.
The two failure modes are different. Losing region files costs you builds and terrain, and players understand that, because the damage is visible. Losing plugin data costs you trust. Ranks disappear, claim ownership detaches from UUIDs, shop chests hold items nobody can prove they owned, and the punishment history you need for a ban appeal is simply absent. Restoring a world from Tuesday while leaving the economy at Friday’s state also duplicates every purchase made in between.
There is a third problem: a world folder alone does not rebuild a server. You need the exact jar, the plugin list with versions, the flags in server.properties, and whatever cron entry ran the restart at 4 AM. That is why the inventory below treats configuration as data, not as something you will “just set up again.”
A Complete Backup Inventory
Work through this table once and add the paths you actually use to your job. The “live copy” column is the important one: it tells you whether a running server will hand you a consistent file.
| Item | Typical path | Safe to copy live? |
|---|---|---|
| World folders and datapacks | world/, world_nether/, world_the_end/ | No, flush first |
| Plugin jars and configs | plugins/*.jar, plugins/*/config.yml | Yes |
| LuckPerms data | plugins/LuckPerms/ or MySQL | Only via database method |
| CoreProtect, shops, economy | plugins/CoreProtect/database.db, MySQL | No |
| Access lists | whitelist.json, ops.json, banned-*.json | Yes |
| Server and Paper config | server.properties, bukkit.yml, config/paper-global.yml | Yes, mostly |
| Units, timers, scripts | /etc/systemd/system/, /etc/cron.d/ | Yes |
| TLS certs and branding | /etc/letsencrypt/, MOTD assets, pack source | Yes, root only |
Flushing the world before you copy it
Region files are written lazily, so a plain copy of a live world can catch a half-written chunk. Send save-off so the server stops writing, then save-all flush so pending writes land on disk, copy, and turn saving back on. On a Paper server that is three console commands, and the window is short enough that nobody notices. Never restore region files while the server is running: it has the old chunks mapped in memory and will happily overwrite your restored data with stale copies.
save-off
save-all flush
# copy world folder, then:
save-on
Databases Need Their Own Method
SQLite in WAL mode keeps recent commits in a separate -wal file until a checkpoint folds them into the main database. Copy the .db file alone and you get a perfectly valid database that is missing the newest rows. Our test made this concrete. A live cp of a 120,000-row database finished in 0.004 seconds and passed integrity_check, yet 1,762 committed rows were absent. Using SQLite’s online backup API took 0.040 seconds, also passed the check, and lost only the 1,150 rows committed after the copy began.
The fix is one line. The SQLite backup API takes a consistent snapshot even while writers are active, and the CLI exposes it as .backup. VACUUM INTO works too and produces a compact copy, and Python’s sqlite3.Connection.backup() is the right tool if you already have a script. Each of these reads the WAL correctly, which cp does not.
sqlite3 plugins/CoreProtect/database.db ".backup '/srv/backup/coreprotect.db'"
sqlite3 plugins/CoreProtect/database.db "VACUUM INTO '/srv/backup/coreprotect-compact.db'"
MySQL and MariaDB
For LuckPerms, shop plugins, and anything else on a real database server, dump with --single-transaction so InnoDB gives you a consistent read without locking players out. Add --routines and --triggers or your stored logic vanishes, and keep binary logs if you want point-in-time recovery. The MariaDB dump documentation covers the flags; our own walkthrough of MariaDB for LuckPerms and CoreProtect covers the schema side.
mysqldump --single-transaction --routines --triggers --databases luckperms coreprotect
> /srv/backup/mysql-$(date +%F).sql
mysql < /srv/backup/mysql-2026-10-22.sql
Build the Backup Job
Use a content-addressed tool rather than a folder of tarballs. restic, Borg, and Kopia all split files into chunks, hash them, and store each chunk once, so a second “full” snapshot of a 249 MiB server costs only what actually changed. With compression on, our first restic backup took 1.178 seconds and produced a 153,851,624-byte repository. We then added one config file and 2,000 database rows. The next run took 0.825 seconds and grew the repository by about 421 KB.
export RESTIC_REPOSITORY=/srv/backup/restic
export RESTIC_PASSWORD_FILE=/etc/restic.pass
restic init
restic backup /srv/minecraft /etc/systemd/system /etc/letsencrypt
--exclude /srv/minecraft/logs --tag nightly
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic check --read-data-subset=5%
Run the database dumps first and let the snapshot pick up the output, so the two are minutes apart at worst. Snapshotting a live SQLite file is no better than cp, which is why the dump step is not optional. The restic documentation explains the retention and check commands in full; our automated backup setup guide shows how to wire this to a systemd timer, and reducing backup size and time covers pruning logs and caches. Our timings came from warm cache on local NVMe, so budget more for a cold cache and an off-site upload.
Follow the 3-2-1 rule: three copies, two media, one off-site. The 3-2-1 strategy explainer is a good sanity check. Encrypt the repository, because plugin databases contain IP addresses, private messages, and payment records.
Test the Restore Before You Need It
An untested backup is a hypothesis. In our drill, restoring a single file out of the restic snapshot took 0.745 seconds, and the SHA-256 of the restored file matched the original exactly. That is the whole point of the exercise: you learn the commands, the password location, and the path layout while nobody is shouting in Discord.
restic restore latest --target /srv/restore-test --include /srv/minecraft/server.properties
sha256sum /srv/restore-test/srv/minecraft/server.properties
Restore into a temporary directory first, never over the live server. Verify checksums, then start a staging instance on a different port with its own directory so you can confirm the world loads, LuckPerms resolves ranks, and the shop plugin reads its tables. Only then do you stop the production server and swap. Our MySQL versus SQLite comparison helps you decide which plugins should even be on a file database.
Quarterly drill checklist
- Confirm the repository opens with the stored password file, not a password from memory.
- Restore the newest snapshot into an empty directory and verify three checksums.
- Boot a staging server on an alternate port and join it.
- Check that ranks, claims, balances, and block logs survived.
- Record how long the full drill took, and fix whatever surprised you.
Pros and Cons of Each Approach
File-level copies
Simple, readable, and restorable by hand with no tooling. But live databases and region files are inconsistent, there is no deduplication, and a folder of nightly tarballs fills a disk fast.
Database dumps
Consistent and portable across hosts, and a dump can be edited before restore. The downside is restore time on large tables and the need to script each engine separately.
Snapshots
Fast, deduplicated, encrypted, and versioned, which is why a full-server snapshot every hour is realistic. The cost is a repository you must learn, and a snapshot of a live database is still only as good as the flush behind it.
Common Mistakes and How to Avoid Them
- Backing up only the world. Add plugin data, access lists, and config to the job the same day you read this.
- Copying a live database. Use
.backup,VACUUM INTO, or a dump. A file that passesintegrity_checkcan still be missing rows. - Excluding plugin jars. Configs are meaningless without the exact plugin build. Record versions or keep the jars.
- Keeping backups on the same disk. A failed NVMe takes the server and the backup together. Push off-site.
- Never testing a restore. Schedule the drill and put it in the staff calendar.
- No encryption. Player IPs and chat logs are personal data. Encrypt at rest and restrict the key.
Frequently Asked Questions
No. The world is usually the largest part, but plugin configs, LuckPerms data, economy and protection databases, access lists, and server.properties all live outside it. In our test the world was 247 MB of a 260 MB backup set, and the missing 13 MB held every permission and balance.
No. In WAL mode recent commits sit in a separate -wal file, so a plain copy can be valid and still miss rows. In our test cp lost 1,762 committed rows while passing integrity_check. Use sqlite3 .backup, VACUUM INTO, or a python sqlite3 backup connection instead.
Nightly at minimum, plus a manual snapshot before every plugin update, map reset, or version upgrade. With restic or Borg you can run hourly instead, because deduplication means an unchanged server adds almost nothing to the repository.
No. Stop the server before restoring region files or databases. A running server holds chunks and open database handles in memory and will overwrite what you just restored. Restore into a temp directory, verify, then swap.
Make the Full Set, Then Prove It Restores
The world is one folder. Your server is the world plus permissions, economy, protection logs, configuration, and the scripts that keep it running, and all of that fits in a set small enough to snapshot hourly. Start this week: add the missing paths to your job, switch database copies to .backup and mysqldump, push the repository off-site, and run one restore drill into a temp directory.
If you are rebuilding the whole backup pipeline, begin with automated Minecraft server backups on Linux, then add the database layer and schedule the quarterly drill. Your players will never notice a backup. They will absolutely notice a restore.

Leave a Reply