Back up a FiveM server and database, and test the restore
mysqldump for oxmysql, server files, encryption, a weekly restore test, and how to give one player their inventory back without rolling back the city.
By Termi
- Backups
- Database
- oxmysql
On this page
- What a FiveM backup has to include
- Find the database oxmysql uses
- Dump the database with mysqldump
- Back up the server files
- Encrypt it before it leaves the machine
- Keep copies off the server
- Run it every night
- Test the restore, or you do not have a backup
- Roll back the whole server
- Restore one player’s inventory without rolling back everyone
- ESX
- QBCore and Qbox
- Errors you will probably hit
- Access denied; you need the PROCESS privilege
- Access denied when using LOCK TABLES
- Unknown collation utf8mb4_0900_ai_ci
- The same, done by FiveDock
- FAQ
- How often should I back up a FiveM server?
- Can I back up the database while the server is running?
- Does txAdmin back up my database?
- How do I roll back a FiveM server after a dupe exploit?
- How do I restore one player’s inventory?
- Where is the database oxmysql uses?
To back up a FiveM server, dump the MySQL or MariaDB database that oxmysql connects to with mysqldump --single-transaction, archive server.cfg, your resources and txData, encrypt both, and copy them off the machine. A backup only counts once you have restored it, so load one into a throwaway database every week. To give one player back their inventory, restore into that throwaway database and copy only their rows.
This guide does all of that by hand with free tools, on Linux or on Windows with Git Bash. Nothing in it needs FiveDock. At the end, the same thing done for you, including Deploy Guard, which takes a backup before every restart.
What a FiveM backup has to include
Leave out cache: FXServer rebuilds it on the next start, and it is often the largest folder on the disk. Logs can go too.
Find the database oxmysql uses
oxmysql reads its connection from server.cfg:
set mysql_connection_string "mysql://fivem:[email protected]/fivem?charset=utf8mb4"That is user fivem, password secret, host 127.0.0.1 and database fivem. Some servers use the older form server=127.0.0.1;database=fivem;userid=fivem;password=secret, which holds the same four values.
Dump the database with mysqldump
Put the credentials in a file only you can read, so the password is not visible in the process list or your shell history:
cat > "$HOME/.fivem-backup.cnf" <<'EOF'
[client]
user=fivem
password=secret
host=127.0.0.1
EOF
chmod 600 "$HOME/.fivem-backup.cnf"Then dump the database while the server keeps running:
mysqldump --defaults-extra-file="$HOME/.fivem-backup.cnf" \
--single-transaction --quick --no-tablespaces \
--default-character-set=utf8mb4 --hex-blob \
fivem | gzip > "fivem-$(date +%F-%H%M).sql.gz"On MariaDB the same command is also called mariadb-dump, with the same options. If your scripts use stored procedures, add --routines.
Check for MyISAM tables
The snapshot only covers InnoDB tables. A MyISAM table is read at a slightly different moment from the rest, so a backup taken during a trade can hold the money from before it and the item from after. ALTER TABLE name ENGINE=InnoDB; converts one.
Back up the server files
tar -czf "files-$(date +%F-%H%M).tar.gz" \
--exclude='server-data/cache' \
-C /home/fivem server-data txDataPoint -C at the folder that holds server-data and txData. On Windows, tar ships with Windows 10 and 11 and takes the same options.
Encrypt it before it leaves the machine
A database dump holds every player’s identifiers, and server.cfg holds the database password and your license key. Anyone who gets a copy can read all of it, or start your server under your key. Encrypt with age and a public key: the server can encrypt backups but cannot open them, so a stolen server does not give away its own backups.
Make the key pair on your own computer, not on the server:
age-keygen -o fivem-backup.key
# Public key: age1q9x...Keep fivem-backup.key in your password manager and give the server only the public key that starts with age1:
age -r age1q9x... -o fivem-2026-10-15-0400.sql.gz.age fivem-2026-10-15-0400.sql.gzLose the private key and every backup it encrypted is gone with it. Keep two copies, in two different places.
Keep copies off the server
A backup on the same disk goes when the disk fails, when the host ends your contract or when someone wipes the machine. Copy every backup to object storage, such as Backblaze B2, Cloudflare R2 or S3, with rclone. Give the server a storage key that can upload files but not delete them, so whoever gets into the server cannot delete your backups as well.
Run it every night
All of the above in one script:
#!/usr/bin/env bash
set -euo pipefail
STAMP=$(date +%F-%H%M)
OUT=/var/backups/fivem
RECIPIENT=age1q9x...
mkdir -p "$OUT"
mysqldump --defaults-extra-file="$HOME/.fivem-backup.cnf" \
--single-transaction --quick --no-tablespaces \
--default-character-set=utf8mb4 --hex-blob \
fivem | gzip | age -r "$RECIPIENT" > "$OUT/db-$STAMP.sql.gz.age"
tar -czf - --exclude='server-data/cache' -C /home/fivem server-data txData \
| age -r "$RECIPIENT" > "$OUT/files-$STAMP.tar.gz.age"
rclone copy "$OUT" b2:my-city-backups --include '*.age'
find "$OUT" -name '*.age' -mtime +14 -deleteset -o pipefail matters more than it looks. Without it, mysqldump | gzip succeeds whenever gzip does, so a wrong password leaves you a 20-byte file every night and a log that says everything worked. With it, the script stops at the first failure.
Run it at 04:00 every day with cron:
0 4 * * * /home/fivem/backup.sh >> /var/log/fivem-backup.log 2>&1On Windows, run backup.sh with Git Bash from Task Scheduler. Take an extra backup by hand before every update to a framework or an inventory script.
Test the restore, or you do not have a backup
Most rollbacks fail on the night they are needed: the dump stopped halfway, the password changed three months ago, nobody kept the private key. A weekly test catches all three. Do it on a machine that has the private key and MySQL or MariaDB, such as your own computer or a test server:
age -d -i fivem-backup.key db-2026-10-15-0400.sql.gz.age | gunzip > restore.sql
tail -n 1 restore.sql
# -- Dump completed on 2026-10-15 4:00:03
mysql -e 'DROP DATABASE IF EXISTS restore_test; CREATE DATABASE restore_test'
mysql restore_test < restore.sqlA complete dump ends with the -- Dump completed line. If it is missing, the dump was cut short. Once it is imported, compare the backup with the live server. Use players instead of users on QBCore and Qbox:
SELECT table_schema, COUNT(*) AS tables
FROM information_schema.tables
WHERE table_schema IN ('fivem', 'restore_test')
GROUP BY table_schema;
SELECT
(SELECT COUNT(*) FROM fivem.users) AS players_now,
(SELECT COUNT(*) FROM restore_test.users) AS players_in_backup;The same number of tables and a player count close to today’s means the backup is whole. To test the files too, unpack the archive next to a copy of FXServer, point it at restore_test and start it.
Roll back the whole server
Use this after a duplication exploit, a broken economy or an update that damaged the database. Every player loses what happened since the backup, so tell them first.
mysqldump --defaults-extra-file="$HOME/.fivem-backup.cnf" \
--single-transaction --quick --no-tablespaces \
--default-character-set=utf8mb4 --hex-blob \
fivem | gzip > before-rollback.sql.gz
age -d -i fivem-backup.key db-2026-10-15-0400.sql.gz.age | gunzip | mysql fivemThe dump drops and recreates each table it contains. A table created after the backup stays as it is.
Restore one player’s inventory without rolling back everyone
A player lost their inventory to a bug, or a cheater emptied their account. Rolling back the city would cost everyone else a day of play. Instead, import the backup into restore_test on the same database server, as in the restore test, and copy only that player’s rows back.
First find their identifier. In ESX it is the identifier column of users, and with ox_inventory their inventory is stored in the same row. In QBCore and Qbox it is citizenid in players. Then kick the player and keep them off the server until you are done, or the framework writes their current state back over the restore at its next save.
ESX
SET FOREIGN_KEY_CHECKS = 0;
START TRANSACTION;
REPLACE INTO fivem.users
SELECT * FROM restore_test.users WHERE identifier = 'char1:3f2a9c...';
DELETE FROM fivem.owned_vehicles WHERE owner = 'char1:3f2a9c...';
INSERT INTO fivem.owned_vehicles
SELECT * FROM restore_test.owned_vehicles WHERE owner = 'char1:3f2a9c...';
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;QBCore and Qbox
SET FOREIGN_KEY_CHECKS = 0;
START TRANSACTION;
REPLACE INTO fivem.players
SELECT * FROM restore_test.players WHERE citizenid = 'KX81F2QD';
DELETE FROM fivem.player_vehicles WHERE citizenid = 'KX81F2QD';
INSERT INTO fivem.player_vehicles
SELECT * FROM restore_test.player_vehicles WHERE citizenid = 'KX81F2QD';
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;REPLACE overwrites the player’s row with the one from the backup: money, inventory and position. The vehicles are deleted and inserted again, so a car bought after the backup goes too. If a car changed hands since the backup, the INSERT stops on a duplicate key and the transaction leaves everything as it was. Decide who keeps it, then run it again.
If the player traded since the backup, restoring them hands back what they already gave away, and the item now exists twice. Check their transactions before you restore someone who claims to have lost everything.
Errors you will probably hit
Access denied; you need the PROCESS privilege
mysqldump: Error: 'Access denied; you need (at least one of) the PROCESS privilege(s) for this operation' when trying to dump tablespaces appears on MySQL 8.0.21 and later. Add --no-tablespaces. A FiveM database has no tablespaces worth dumping.
Access denied when using LOCK TABLES
mysqldump: Got error: 1044: Access denied for user 'fivem'@'localhost' to database 'fivem' when using LOCK TABLES means the dump ran without --single-transaction and tried to lock every table. Add the option. It is also the reason the dump no longer freezes the server.
Unknown collation utf8mb4_0900_ai_ci
ERROR 1273 (HY000) at line 25: Unknown collation: 'utf8mb4_0900_ai_ci' means a MySQL 8 dump is going into MariaDB, which does not know that collation. Replace it before importing:
sed -i 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g' restore.sqlThe same, done by FiveDock
Everything above is what FiveDock’s Time Machine does from inside your server, through the same agent that runs the rest of FiveDock. There is no script to maintain and no cron job to forget.
Every plan takes a daily backup. Restoring one table or one player, weekly test restores, hourly backups and Deploy Guard come with Metro. Pricing has the full list, and features shows the rest of Time Machine.
FAQ
How often should I back up a FiveM server?
Back up the database at least once a day, and once more by hand before every framework or inventory update. If your economy is busy, back up every hour: a duplication exploit found at 22:00 against a 04:00 backup costs every player 18 hours.
Can I back up the database while the server is running?
Yes. mysqldump --single-transaction reads InnoDB tables in one consistent snapshot without locking them, so players notice nothing. Only MyISAM tables fall outside the snapshot.
Does txAdmin back up my database?
No. txAdmin starts, restarts and watches the server, but it does not dump your MySQL or MariaDB database. Use mysqldump on a schedule, or a tool that does it for you.
How do I roll back a FiveM server after a dupe exploit?
Stop the server in txAdmin, take a backup of the current state, import the last backup from before the exploit and start the server again. If only a few players profited, restore or clean their rows instead of rolling back everyone.
How do I restore one player’s inventory?
Import the backup into a separate database on the same server, keep the player offline, and copy their rows from users (ESX) or players (QBCore and Qbox), plus their vehicles, into the live database in one transaction.
Where is the database oxmysql uses?
In server.cfg, on the set mysql_connection_string line. It holds the user, password, host and database name that mysqldump needs.