RE:NODE

QBCore to Qbox migration: bridge, inventory and database

By RE:NODE Team
14 min read


Moving a QBCore server to Qbox is mostly a database and inventory job, not a scripting one. qbx_core declares provide 'qb-core' and ships a bridge that answers the old exports['qb-core']:GetCoreObject() calls, so most qb-* job, shop and HUD resources start without edits. What does not carry over is qb-inventory: Qbox runs on ox_inventory only and refuses to work with qb-inventory running, so item definitions, every player's inventory and your stashes, trunks and gloveboxes need converting. On top of that, qbx_core.sql changes the players table and adds a player_groups table for multijob. Do all of it on a copy of the server and database first, keep the old resources/ folder and a database dump untouched, and the switch itself is an evening rather than a week.

Which framework to pick in the first place, and what each one is, is covered in FiveM frameworks: ESX vs QBCore vs Qbox. This post assumes you have already decided and have a running QBCore server with real player data you do not want to lose.

What actually changes between QBCore and Qbox#

Qbox is a continuation of QBCore, not a rewrite from nothing, which is why a migration is possible at all. The player object still has PlayerData, citizenid, charinfo, money, job and gang. The players table keeps its name and most of its columns. What moved is everything around the core.

AreaQBCoreQbox
Core resourceqb-coreqbx_core (also answers to qb-core)
Inventoryqb-inventoryox_inventory, nothing else
Item listqb-core/shared/items.luaox_inventory/data/items.lua
Jobs and gangsshared/jobs.lua, gangs.luathe same files, numeric grade keys
Multijoban external resourcebuilt in, player_groups table
Multicharacterqb-multicharacterbuilt into qbx_core
Target, menus, inputqb-target, qb-menu, qb-inputox_target and ox_lib

The last three rows are where most of your work hides. Qbox also requires ox_lib and oxmysql to be running first, OneSync on, and a recent FXServer build. The current qbx_core manifest lists /server:10731 as a dependency, which means it will not start on an older artifact - the number moves with releases, so read fxmanifest.lua in the version you download, and see FiveM server updates and artifacts if your server is behind.

How the qb-core bridge works, and where it stops#

The bridge is a set of files under qbx_core/bridge/qb/ that rebuild the old QBCore object out of Qbox's own functions. Because qbx_core declares provide 'qb-core' in its manifest, a resource that lists qb-core as a dependency is satisfied, and exports['qb-core']:GetCoreObject() returns something that looks like the QBCore object. The bridge is on by default and controlled by one convar:

server.cfg
setr qbx:enableBridge "true"

Things that keep working through the bridge:

  • QBCore.Functions.GetPlayer(source) and the player methods hanging off it: AddMoney, RemoveMoney, SetJob, SetMetaData and the rest.
  • The usual server and client events a job resource listens for, such as QBCore:Client:OnPlayerLoaded.
  • QBCore.Shared.Jobs, Gangs and Vehicles, read from Qbox's own files.
  • The old drawtext exports, exports['qb-core']:DrawText() and HideText(), now mapped onto ox_lib text UI.
  • Item functions on the player such as Player.Functions.AddItem and RemoveItem, which the bridge forwards to ox_inventory and translates between the two item formats.

The Qbox documentation describes the framework as about 99% compatible with QB scripts, and for the core object that is fair. Where it stops is anything that reaches past the core:

  • Anything calling `qb-inventory` directly. exports['qb-inventory']:..., the inventory:server:OpenInventory event and qb-inventory's stash events have nothing answering them. The bridge's own inventory functions assert that qb-inventory is not started and fail with qb-inventory is not compatible with qbx_core. use ox_inventory instead.
  • `Player.Functions.SetInventory` is explicitly unsupported and throws. Scripts that rewrite a whole inventory need changing to clear-then-add.
  • Resources that write SQL against QBCore tables that no longer mean the same thing - old stash tables, a job column that multijob now shadows, player_vehicles rows that a garage resource expects in a particular shape.
  • External multijob and multigang resources. Qbox has its own, and the documentation warns that unconfirmed ones can corrupt data. Remove them before the first start.
  • `qb-target` exports. ox_target provides compatibility for qtarget, not for qb-target. A resource calling exports['qb-target']:AddBoxZone needs qb-target still running or converting to ox_target.

The bridge is a migration tool, not a permanent home. Its functions are marked deprecated in the source, and new code should call exports.qbx_core and ox_lib directly. You can convert one resource at a time, because the bridged and native calls work side by side.

Audit the resource list before touching anything#

Open resources/ and sort every folder into three piles. This is an hour with a text file and it decides how long the rest takes.

  1. Works unchanged. Resources that only use the core object, player money, jobs and events. Most HUDs, simple jobs, shops that sell through AddItem, emotes, weather, voice.
  2. Has a Qbox or ox replacement. Usually the better route than patching. Qbox maintains qbx_ versions of the commonly used QB resources.
  3. Needs editing or dropping. Anything that talks to qb-inventory, uses qb-menu or qb-input heavily, or writes its own SQL against inventory and vehicle tables.

The replacements that come up on almost every server:

QBCore resourceQbox sideNote
qb-inventoryox_inventoryRequired, data must convert
qb-targetox_targetZones must be rewritten
qb-menu, qb-inputox_libContext menus and input dialogs
qb-doorlockox_doorlockDoors are re-entered
qb-multicharacterqbx_coreBuilt in
qb-garagesqbx_garagesUses qbx_vehicles
qb-vehiclekeysqbx_vehiclekeys
qb-policejobqbx_police
qb-ambulancejobqbx_ambulancejobPairs with qbx_medical

ox_inventory's Qbox integration also asserts that qbx_vehicles is running, so add it even if you keep your own garage resource. For paid scripts, check the author's page before assuming anything: many advertise Qbox support through a config switch, and an escrowed script you cannot edit either supports Qbox or it does not. FiveM escrow and asset licensing covers what you can and cannot change in those.

The fastest way to build pile three is a search. Run this from the resources/ folder on a copy of the server and read every hit:

bash
$ grep -rn --include=*.lua -e "qb-inventory" -e "qb-target" -e "SetInventory" .$ grep -rn --include=*.lua -e "stashitems" -e "trunkitems" -e "gloveboxitems" .$ grep -rn --include=*.lua -e "QBCore.Shared.Items\[" .

The last one matters because some resources add items at runtime by writing into QBCore.Shared.Items. Under Qbox, items belong to ox_inventory, and those runtime additions do nothing useful.

Build the test server first#

Do not migrate the live server in place. Build a second server, give it a copy of the database and a copy of resources/, and do every step below there first. The general method - what to copy, what must differ, how small it can be - is in running a test server beside production. For this migration specifically:

  • A separate database, always. The test server must point its mysql_connection_string at a copy, never at the production schema. The SQL steps below alter tables, and running them against live data by accident is the one mistake that turns an evening into a weekend.
  • A fresh dump of production to seed it. Use mysqldump with --single-transaction so you do not lock players out while it runs, as described in mysqldump backup and restore.
  • Close it to the public. Set sv_maxclients low, add a password-style check through your Discord whitelist or simply do not list it, and give it its own hostname so nobody connects to the wrong one.
  • Log in as a real character. Testing on a fresh character proves nothing about conversion. Pick a few characters with full inventories, weapons, a car with items in the trunk and a job grade above zero, and check every one of them after conversion.

On RE:NODE each server is its own container with its own database slot, so the test server's copy cannot see the production database unless you give it those credentials. Import the dump through "Open in phpMyAdmin" on the test server's database tab, or from the command line with the generated host and user.

Database changes Qbox makes#

Back up first. Every step in this section is easy to apply and awkward to reverse.

bash
$ mysqldump --single-transaction --routines -h DB_HOST -u DB_USER -p qbcore \    > qbcore-before-qbox.sql

Then import qbx_core.sql from the qbx_core release you are installing. Against an existing QBCore database it does three things that matter. It adds a last_logged_out column (and in current versions a userId column) to players. It changes players so the table and its key columns use the utf8mb4_unicode_ci collation. And it creates player_groups, which stores every job and gang a character holds, with a foreign key on citizenid back to players.

That foreign key is where most migrations stall. A foreign key needs both columns to use the same collation, and plenty of QBCore databases were created with utf8mb4_general_ci. If the import fails with a foreign key error, check and fix the collation:

sql
SELECT TABLE_NAME, COLUMN_NAME, COLLATION_NAMEFROM information_schema.COLUMNSWHERE TABLE_SCHEMA = 'qbcore' AND COLUMN_NAME = 'citizenid';ALTER TABLE players  MODIFY citizenid VARCHAR(50) NOT NULL COLLATE utf8mb4_unicode_ci;

Run the same MODIFY on any other table whose citizenid column still reports utf8mb4_general_ci, keeping each column's existing type and nullability, then import qbx_core.sql again. The reasons collations clash at all are covered in MySQL utf8mb4 and collations.

Next, the jobs and gangs. Qbox wants grade keys in shared/jobs.lua and shared/gangs.lua as numbers, where QBCore used strings:

qbx_core/shared/jobs.lua
police = {    label = 'LSPD',    defaultDuty = true,    grades = {        [0] = { name = 'Recruit', payment = 50 },      -- QBCore had ['0']        [1] = { name = 'Officer', payment = 75 },        [4] = { name = 'Chief', isboss = true, payment = 150 },    },},

Copy your custom jobs and gangs across before the first start, then fill the new table. With the server running, type this in the txAdmin or live console:

code
convertjobs

It copies each character's current job and gang from the players table into player_groups, it only runs from the server console, and it is safe to run twice. Two convars decide how multijob behaves afterwards: qbx:max_jobs_per_player and qbx:max_gangs_per_player, both 1 by default.

player_vehicles is left alone: qbx_vehicles creates it only if it does not exist. ox_inventory adds a trunk and a glovebox column to it, and an inventory column to players if one is missing, on its first start.

Converting qb-inventory to ox_inventory#

This is the part that needs the most care, and the part where tutorials disagree most, because the tools changed in 2024. Three separate things move: item definitions, player inventories, and everything else (stashes, trunks, gloveboxes).

Item definitions

Paste your QBCore item table into qbx_core/shared/items.lua. On the next start the bridge converts every item that ox_inventory does not already know into ox_inventory's format and writes them to qbx_core/converted.lua, with a console warning telling you how many it added. It skips names that look like weapons, attachments and ammo, because ox_inventory has its own weapon definitions. Review the file, copy the entries into ox_inventory/data/items.lua, empty shared/items.lua again, and restart. A converted entry looks like this:

ox_inventory/data/items.lua
['sandwich'] = {    label = 'Sandwich',    weight = 200,    stack = true,    close = true,    description = 'Nice bread for your stomach',    client = {        status = { hunger = 200000 },        image = 'sandwich.png',    },},

Usable items still work through the bridge's CreateUseableItem, but the hunger and thirst values from your consumables resource will not apply themselves; check each food item once in game. Copy item images from qb-inventory/html/images into ox_inventory/web/images, or every converted item shows a blank square.

Player inventories

QBCore and ox_inventory both store a player's inventory as JSON in players.inventory, but the fields differ:

json
{"name": "water_bottle", "amount": 3, "info": {}, "slot": 1, "type": "item"}{"name": "water_bottle", "count": 3, "metadata": {}, "slot": 1}

The first line is QBCore, the second ox_inventory. When ox_inventory loads an unconverted character it stops with Inventory for player.X contains invalid data. Ensure you have converted inventories to the correct format. Current ox_inventory releases ship a convertinventory console command, but its only options are esx and esxproperty; run it with no argument and it prints that list. The QBCore converter was removed in August 2024, and v2.41.0 is the last release that has convertinventory qb. It renamed amount to count and info to metadata, turned QBCore cash into the money item, and gave weapons a serial and durability from their old quality.

The practical routes are to run that old release once on the test server purely to convert, then replace it with a current ox_inventory, or to write the same field mapping yourself in SQL or Lua. Either way, compare a handful of characters before and after. Once converted, cash lives in two places that ox_inventory keeps in sync: the money item and PlayerData.money.cash.

Stashes, trunks and gloveboxes

This is where data is most often lost. Older qb-inventory versions kept these in stashitems, trunkitems and gloveboxitems; the old converter moved trunks and gloveboxes into player_vehicles but never handled stashes. Current qb-inventory (2.x) stores all of them in one inventories table keyed by an identifier string, which no official converter reads. Under Qbox, ox_inventory stores stashes in its own ox_inventory table and trunks and gloveboxes in player_vehicles, keyed by the vehicle's id.

So decide what is worth keeping. Personal and job stashes usually are, and they need a small script that reads each inventories row, converts the items as above and inserts them under the stash name your ox_inventory stash definitions use. Many servers decide trunk and glovebox contents are not worth a script and announce they will be emptied. That is a legitimate choice, but make it on purpose and tell players before the switch, not afterwards.

Cutover day, in order#

Once the test server has run the whole process and survived a session with real testers, the live switch is a checklist:

  1. Announce a maintenance window and close the server with txAdmin's whitelist or by stopping it.
  2. Take a final mysqldump and a panel backup of the server files. Download both.
  3. Run the SQL steps, in the order that worked on the test server.
  4. Swap resources/ for the tested folder, and bring across server.cfg, ox.cfg and permissions.cfg from it.
  5. Start with the console open. ensure oxmysql, ox_lib, qbx_core and ox_inventory must come up before anything that depends on them. Reading the console covers what to look for.
  6. Run convertjobs and the inventory conversion you rehearsed.
  7. Log in as three of the characters you checked on the test server. Open the inventory, a stash and a trunk.
  8. Open to players, and watch the console and resmon for the first hour.

Rollback plan#

Write the rollback down before you start, because the moment you need it is the moment you will not think clearly. A migration is reversible only if both halves of the old server still exist untouched.

  • Files. Keep the old resources/ folder and server.cfg as an archive, not overwritten. Switching back is restoring that folder.
  • Database. Restore qbcore-before-qbox.sql into the database. A forward fix is not a rollback - the converted inventory JSON, the new collation and player_groups will not undo themselves.
  • A decision deadline. Pick the point after which you fix forward instead. Players who have played on Qbox for a day have data a restore would erase. A sensible rule: roll back freely during the maintenance window, and only for data loss after it.

On RE:NODE the panel backup covers files and the dump covers the database, so take both. A backup can be locked against rotation so the pre-migration copy survives your scheduled backups, but test the restore on the test server before you rely on it.

Troubleshooting#

The errors that come up in nearly every migration:

SymptomCause
qb-inventory is not compatible with qbx_coreqb-inventory still started
contains invalid data on loginInventory still in QBCore format
Foreign key error importing qbx_core.sqlcitizenid collations differ
Job grade shows as nilGrade keys still strings
Items show blank squaresImages not copied
Players lost their jobcleanPlayerGroups on with jobs missing

Two more are quieter. A resource that silently does nothing usually calls QBCore.Shared.Items to check an item exists; replace that lookup with exports.ox_inventory:Items(). And duplicate folders - an old qb-inventory in a bracketed folder you forgot about - start whichever FXServer finds first, so delete the old one rather than leaving it unensured.

FAQ#

Can I run Qbox without the qb-core bridge?

Yes, set qbx:enableBridge to false, but only once nothing on the server calls qb-core any more. Most servers keep it on for months and convert resources gradually.

Do I lose character data moving to Qbox?

Not if you convert properly. Characters, money, metadata, jobs and vehicles stay in the same tables. Inventories, stashes, trunks and gloveboxes are the data at risk, because they change format and tables.

Can I keep qb-inventory on Qbox?

No. qbx_core refuses to work with qb-inventory running, and ox_inventory is the only inventory Qbox supports.

Will my paid QBCore scripts work on Qbox?

Many do through the bridge, and many authors ship a Qbox option. The ones that break are those that call qb-inventory or qb-target directly. Check the author's notes and test on the copy.

How long does a migration take?

A server with a modest resource list is a weekend of preparation on the test server and a one or two hour maintenance window. Large servers with dozens of inventory-dependent scripts take longer, mostly in rewriting those scripts.

Can I go back to QBCore after switching?

Only by restoring the pre-migration database dump and the old resources/ folder, which erases anything players did after the switch. Decide your rollback cut-off before you start.