A practical guide to how to set up a fivem qbcore server from scratch, with implementation decisions, validation steps, and security considerations for a production-minded project.
This guide gives a complete, practical walkthrough to set up a FiveM server running QBCore from scratch, with architecture decisions, security and validation best practices, implementation notes covering QBCore/ESX/ox_lib integrations, Roblox parallels for server authority and DataStore handling, testing, deployment, and maintenance. Use this as a reference checklist and implementation template rather than a drop-in script. Always keep server authority and validation central: client input must be treated as untrusted on FiveM, and Roblox scripts must respect server-side validation and robust DataStore handling.
High-level architecture and resource layout
Design the server in layers: core framework (QBCore/ESX), shared utilities (ox_lib, utility resources), gameplay resources (jobs, inventory, vehicles), and infrastructure resources (database connector, logging, permissions). Keep resource boundaries clear so you can restart or replace modules without touching unrelated systems.
- Core: qb-core or ESX base resource, event exporters, server callbacks.
- Database: oxmysql or ghmattimysql / mysql-async adapter resource that mediates access to MySQL.
- Libraries: ox_lib for menus/exports, ox_inventory or qb-inventory for item handling.
- Gameplay modules: job scripts, vehicle spawning handlers, police utils, stores.
- Admin tools: txAdmin, webadmin, rcon/logging export.
Example minimal fxmanifest pattern for a resource:
fx_version 'cerulean'
game 'gta5'
author 'you'
description 'example-job'
version '1.0.0'
shared_scripts {
'@qb-core/shared/locale.lua',
'config.lua'
}
server_scripts {
'@oxmysql/lib/MySQL.lua', -- or ghmattimysql
'server/main.lua'
}
client_scripts {
'client/main.lua'
}
Database design and migrations
Use normalized tables for inventories, characters, vehicles, and permissions. Migrations should be versioned and idempotent. Use parameterized queries always; never construct SQL with direct concatenation of client-sent strings.
- Tables: players, characters, items, vehicles, logs, sessions.
- Indexes: add indexes on frequently queried columns (identifier, citizenid, job).
- Migrations: store a schema_version table and write migrations that can be re-run safely.
Security and server validation
Security is a combination of architecture, code discipline, and runtime defense. For FiveM, treat all client-originated data as untrusted and perform full server-side validation for every sensitive action. In Roblox, authority must be on the server for state changes; clients are only requesters.
- Never trust client input: validate item IDs, quantities, funds, and ownership on the server before committing anything.
- Use parameterized queries: protect against SQL injection by using the DB library placeholders (oxmysql/ghmattimysql supplies parameter support).
- Permissions and roles: store permission levels server-side and verify before privileged actions.
- Rate-limiting and anti-spam: throttle repeated actions (e.g., pickup, buy) per player session.
- Data integrity: use transactions for multi-step DB writes where supported.
Server-side validation example (QBCore server-side Lua):
-- server/main.lua
QBCore = exports['qb-core']:GetCoreObject()
RegisterNetEvent('shop:buyItem', function(itemId, amount)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
-- Validate amount type and bounds
if type(amount) ~= 'number' or amount < 1 or amount > 100 then
-- invalid, ignore and log
return
end
-- Validate item exists in server-side config
local itemDef = ServerItemConfig[itemId]
if not itemDef then return end
local totalPrice = itemDef.price * amount
if Player.PlayerData.money['bank'] < totalPrice then
return -- insufficient funds
end
-- Final server-side transaction
Player.Functions.RemoveMoney('bank', totalPrice)
Player.Functions.AddItem(itemId, amount)
end)
Implementation choices: QBCore vs ESX and ox_lib
QBCore and ESX are different design philosophies. QBCore favors lightweight modularity and exports; ESX is more monolithic historically. Choose based on resource compatibility and team familiarity. ox_lib provides client-side UI and server exports that many modern resources rely on—treat it as a library dependency with clear version control and update strategy.
- Prefer QBCore if you want modular exports and a modern flow (server callbacks and player wrappers).
- Choose ESX if you must use legacy resources unless you plan to convert them incrementally.
- Use ox_lib for menus, notifications, and easier client-server utility exports, but keep all authoritative logic on the server.
Inventory and item systems
Pick an inventory system (ox_inventory, qb-inventory) that matches your persistence model. For stackable items, enforce server-side stacking rules and size limits. Apply anti-duplication checks on item creation and transfers.
Roblox parallels: server authority, RemoteEvents, and DataStore failure handling
Roblox development shares many of the same central principles: the server must be authoritative, RemoteEvents/RemoteFunctions are transport only, and DataStore operations must gracefully handle failures and rate limits.
- Server authority: do all state mutations on server scripts. Use client requests only to initiate actions. Never allow the client to alter critical state directly.
- RemoteEvent pattern: client -> server: validate parameters and permissions; server -> client: send sanitized view-only state.
- DataStore reliability: implement retry with exponential backoff, use UpdateAsync carefully, and use BindToClose to persist data on shutdown.
Roblox DataStore retry example (Luau):
local DataStoreService = game:GetService('DataStoreService')
local PlayersStore = DataStoreService:GetDataStore('Players')
local function savePlayerData(userId, data)
local retries = 0
local success, err
repeat
success, err = pcall(function()
PlayersStore:UpdateAsync(tostring(userId), function(old)
return data
end)
end)
if not success then
retries += 1
task.wait(2 ^ math.min(retries, 5)) -- exponential backoff capped
end
until success or retries >= 5
if not success then
warn('Failed saving for', userId, err)
end
end
Testing, debugging, and QA
Test at multiple levels: unit tests for pure logic where possible, integration tests for DB interactions, and load tests for connection and session behaviors. Use txAdmin and cfx.re console logs for runtime issues. Create local staging environments that mirror production MySQL and resource ordering.
- Automated tests: validate business logic functions (Lua unit tests via busted if applicable).
- Integration tests: simulate player sequences for login, purchases, vehicle spawns.
- Load testing: use bots or scripted clients to simulate connection spikes and resource restarts.
- Runtime debugging: use dbg prints, structured JSON logs, and txAdmin live console for crash traces.
Deployment and maintenance
Deploy with reproducible builds and versioned resources. Use CI to push resource artifacts and a robust restart strategy to minimize downtime. Back up databases and automated snapshots daily, and keep schema migrations backward-compatible when possible.
Tools and best practices:
- Use Git for all resources and tag releases.
- Use txAdmin for process management, auto-restarts, and scheduled backups.
- Automate database backups with retention policies and test restores regularly.
- Document upgrade steps and perform blue-green deploys when touching core database changes.
For hands-on deployment dashboards and workspace integration, consider using the Stellar AI tools: https://trystellarai.com/app for project organization. Integrate server logs and monitoring into your deployment pipeline and keep an eye on critical error rates; coordinating tools can help — check https://trystellarai.com/app for workflow options and the team blog at https://trystellarai.com/blog for operational guides.
Monitoring, logging, and incident response
Structured logs and error aggregation are essential. Log important security events (failed validation, suspicious actions). Use tags to filter per-player or per-resource problems, and keep an incident playbook for rollbacks and hotfixes.
- Log levels: debug, info, warn, error. Send error/warn to an aggregator.
- Alerting: set alerts on high error rates, repeated validation failures, and long DB queries.
- Incident playbook: immediate mute/ban steps, how to roll back a migration, and emergency restart scripts.
Practical checklist
| Task | Why | Completed |
|---|---|---|
| Install QBCore / base | Provides player abstractions, callbacks, and core exports | [ ] |
| Set up MySQL + oxmysql | Persistent storage with safe parameterized queries | [ ] |
| Implement server-side validation | Prevents duplication, fraud, and exploits | [ ] |
| Configure txAdmin and backups | Process management and automated backups | [ ] |
| Test resource restart/resilience | Ensure safe hot-reloads and dependency ordering | [ ] |
| Monitor logs & alerts | Early detection of regressions or abuse | [ ] |
Maintenance and upgrades
Keep a scheduled maintenance window for upgrades. Always run migrations against a clone of production first. Keep a changelog and rollback steps published to your team. For third-party libraries like ox_lib, track upstream releases and test them in staging before deploying to production.