ESX to QBCore Migration Guide
Switching from ESX to QBCore opens the door to modern APIs, modular resources, and an active development community. This guide outlines a staged approach so you can migrate safely without losing player data or custom gameplay systems.
Why teams migrate
- ✅ Modern architecture – shared callbacks, metadata, and better exports
- ✅ Active ecosystem – maintained default jobs and utilities
- ✅ Security-focused – built-in safeguards against common exploits
- ✅ Extensible – Lua modules, UI patterns, and community tooling
Pre-migration checklist
- Dedicated staging server mirroring production hardware
- Full backup of ESX resources and MySQL database
- Inventory of custom scripts and dependencies
- Freeze on new feature deployments during migration window
- Communication plan for staff and players
⚠️
Never migrate directly on a live server. Always validate the process on a staging environment and keep a rollback path ready.
Framework concept mapping
ESX Concept | QBCore Equivalent | Notes |
---|---|---|
xPlayer object | QBCore.Functions.GetPlayer() | Metadata stored in PlayerData |
Accounts (bank , money ) | Money accounts (cash , bank ) | Access via Player.Functions.GetMoney(type) |
Job grades (job.grade_name ) | PlayerData.job.grade.name | Supports duty toggle and permissions |
ESX events (esx:* ) | QBCore events (QBCore:* ) | Many renamed and namespaced |
Items (esx_addoninventory ) | qb-core/shared/items.lua | Uses metadata for variants |
Datastore | Player metadata / global states | Convert JSON blobs |
Phase 1 – Prepare QBCore baseline
- Clone the latest QBCore repository into
resources/[qb]/qb-core
- Install essential resources (
qb-inventory
,qb-clothing
,qb-phone
, etc.) - Configure
server.cfg
with QBCore load order (core → dependencies → jobs) - Import the default QBCore SQL schema into a new database (e.g.,
qbcore_migration
) - Copy custom assets (vehicles, props) that will remain in use
Phase 2 – Migrate player data
1. Create staging tables
CREATE TABLE qbcore_players LIKE qbcore.players;
ALTER TABLE qbcore_players ADD COLUMN esx_identifier VARCHAR(60);
2. Transform ESX player rows
INSERT INTO qbcore_players (citizenid, license, name, money, job, gang, position, metadata, esx_identifier)
SELECT
LOWER(HEX(UUID_TO_BIN(UUID()))) AS citizenid,
REPLACE(identifier, 'steam:', '') AS license,
CONCAT(firstname, ' ', lastname) AS name,
JSON_OBJECT('cash', money, 'bank', bank) AS money,
JSON_OBJECT('name', job, 'grade', job_grade) AS job,
JSON_OBJECT('name', 'none', 'grade', 0) AS gang,
JSON_OBJECT('x', position_x, 'y', position_y, 'z', position_z) AS position,
JSON_OBJECT('lastplayed', last_login, 'phone', phone_number) AS metadata,
identifier AS esx_identifier
FROM es_extended.users;
3. Migrate owned items
INSERT INTO players_inventory (citizenid, items)
SELECT
p.citizenid,
JSON_ARRAYAGG(JSON_OBJECT('name', i.item, 'amount', i.count, 'metadata', JSON_OBJECT()))
FROM esx_addoninventory.items i
JOIN qbcore_players p ON p.esx_identifier = i.owner
GROUP BY p.citizenid;
Review data manually—ensure money balances, job grades, and inventory counts look correct.
Phase 3 – Convert resources
- Replace ESX shared utilities with QBCore equivalents (
QBCore.Shared
,QBCore.Functions
) - Update event names using the mapping table below
- Rewrite server callbacks to use
QBCore.Functions.CreateCallback
- Replace ESX menu/NUI implementations with QBCore UI helpers where possible
- Adjust permissions to use QBCore job grades or metadata flags
Event mapping quick reference
ESX Event | QBCore Replacement |
---|---|
esx:getSharedObject | exports['qb-core']:GetCoreObject() |
esx:playerLoaded | QBCore:Server:OnPlayerLoaded |
esx:playerDropped | QBCore:Server:OnPlayerUnload |
esx:addInventoryItem | Player.Functions.AddItem |
esx:removeInventoryItem | Player.Functions.RemoveItem |
TriggerEvent('esx:getSharedObject', cb) | local QBCore = exports['qb-core']:GetCoreObject() |
Example conversion
-- ESX
ESX.RegisterServerCallback('myresource:getPlayer', function(source, cb)
local xPlayer = ESX.GetPlayerFromId(source)
cb(xPlayer.getIdentifier())
end)
-- QBCore
QBCore.Functions.CreateCallback('myresource:getPlayer', function(source, cb)
local Player = QBCore.Functions.GetPlayer(source)
cb(Player and Player.PlayerData.citizenid)
end)
Phase 4 – Functional testing
- Use Resmon Basics to profile the staging server
- Verify money, inventory, job changes, and metadata updates in-game
- Run through mission-critical scripts (police, EMS, economy)
- Invite staff for scenario-based testing (robberies, crafting, housing)
Document any missing features and decide whether to recreate them in QBCore or retain legacy ESX components temporarily.
Phase 5 – Production rollout
- Schedule downtime and notify players
- Backup ESX production database immediately before maintenance
- Restore the validated QBCore database backup
- Deploy converted resources and verify load order
- Monitor logs, resmon, and error reports closely for the first 48 hours
Post-migration tasks
- Update onboarding guides and staff documentation
- Train developers on QBCore-specific patterns like Safe Server Events
- Track feedback via Discord/forums and adjust configs quickly
- Plan incremental enhancements now that QBCore is live
✅
Celebrate once the migration is stable—but keep a retrospective log so future upgrades (like QBCore v1 → v2) are even smoother.