Engineering
FiveM server optimization — a practical performance guide
Diagnose FiveM lag properly: read resmon, kill per-frame loops, index your database, tune OneSync and entity limits, and stop streamed assets stuttering.
First: which lag do you actually have?
"The server is lagging" describes at least four unrelated failure modes. Fixing the wrong one wastes weeks, so start by classifying it.
| Symptom | Almost certainly | Where to look |
|---|---|---|
| Low FPS for everyone, everywhere | Client-side resource cost | resmon 1 on a client |
| Server ms climbs as players join | Server-side per-tick work or DB queries | Server console, txAdmin performance chart |
| Rubber-banding, desync, vehicles teleporting | Network or OneSync entity pressure | Player ping, entity counts, routing buckets |
| Stutter when driving into an area | Streamed assets loading | Your stream folder, MLO and vehicle packs |
| Freezes for a second, then resumes | A blocking database query | oxmysql slow-query log |
Reading resmon like a developer
Type `resmon 1` in the client F8 console. You get a live table of every resource with CPU time, memory, and how many streamed assets it owns. The number that matters is the millisecond column, and the threshold is lower than most people think.
- Anything above 0.10 ms at idle deserves a look. A HUD should sit near 0.01 ms when nothing is happening.
- Above 0.50 ms sustained is a genuine problem — that is a resource actively eating frame time.
- Watch resmon while you play, not while standing still. Many scripts only misbehave in a vehicle, near a marker, or with the inventory open.
- Sort by memory too. A resource holding hundreds of megabytes is usually leaking textures or never releasing a NUI.
The number one cause: per-frame loops
Almost every badly performing FiveM script has the same shape — an infinite thread with `Citizen.Wait(0)` doing distance checks or drawing markers for the whole map. It runs 60+ times per second, for every player, forever, whether or not anything is nearby.
-- BAD: runs every frame, checks 40 locations, forever
CreateThread(function()
while true do
Wait(0)
local coords = GetEntityCoords(PlayerPedId())
for _, shop in pairs(Config.Shops) do
if #(coords - shop.coords) < 2.0 then
DrawMarker(...)
-- help text, key checks, etc.
end
end
end
end)The fix is to make the loop adaptive: sleep long when nothing is close, and only drop to per-frame work when the player is genuinely near something interactive. Better still, delete the loop entirely and use a target system or ox_lib points, which handle proximity for you in one shared, optimised thread.
CreateThread(function()
while true do
local sleep = 1000
local coords = GetEntityCoords(cache.ped)
for _, shop in pairs(Config.Shops) do
local dist = #(coords - shop.coords)
if dist < 20.0 then
sleep = 0
if dist < 2.0 then DrawMarker(...) end
end
end
Wait(sleep)
end
end)for _, shop in pairs(Config.Shops) do
lib.points.new({
coords = shop.coords,
distance = 20,
onEnter = function() -- start drawing
end,
onExit = function() -- stop drawing
end,
})
end- Cache PlayerPedId(). Calling it every frame in every resource adds up; ox_lib's `cache.ped` exists for this reason.
- Never run a per-frame loop for something that changes once a minute — hunger, weather, job checks.
- One shared thread beats twenty small ones. Thread scheduling itself has a cost.
Server ms: what makes the server thread stall
The FiveM server runs its main logic on one thread. Anything that blocks it blocks everyone. Server ms climbing with player count almost always comes from one of four places.
- Synchronous database queries. Any query that is awaited inside a hot path serialises the whole server behind your slowest SQL.
- Events fired at every player. `TriggerClientEvent(name, -1, payload)` with a large payload, several times a second, is a broadcast storm.
- Per-player server loops. A thread iterating every online player every tick scales quadratically with your player count.
- Unbounded entity creation. Spawning vehicles or props server-side without cleanup fills the entity pool until sync degrades.
-- BAD: one event per player per tick
for _, playerId in ipairs(GetPlayers()) do
TriggerClientEvent('hud:update', playerId, BuildFullState())
end
-- GOOD: state bags replicate automatically and only on change
Player(src).state:set('hunger', value, true)Database queries are a performance feature
oxmysql logs slow queries. Turn that on and read it. A `SELECT` against a 200,000-row vehicles table with no index on `owner` will take 300 ms, and during those 300 ms your server is doing nothing else.
ALTER TABLE users ADD INDEX idx_identifier (identifier);
ALTER TABLE owned_vehicles ADD INDEX idx_owner (owner);
ALTER TABLE owned_vehicles ADD INDEX idx_plate (plate);
ALTER TABLE ox_inventory ADD INDEX idx_owner (owner);
ALTER TABLE player_outfits ADD INDEX idx_citizenid (citizenid);- Index every column you filter or join on. Check with EXPLAIN before and after.
- Do not save on a timer for every player at the same moment. Stagger persistence, or save on meaningful events.
- Avoid SELECT *. Fetch the three columns you need, not the JSON blob you do not.
- Keep the database on the same machine or the same private network. A 20 ms round trip to a remote host is 20 ms of server thread, every query.
OneSync, entity limits and routing buckets
OneSync Infinity moves entity ownership server-side and raises the population ceiling. The defaults are generous and most servers never touch them, then blame "OneSync" when sync degrades at 100 players.
- Tune `sv_maxClients` honestly. Advertising 128 slots on hardware that degrades at 70 is worse than advertising 64.
- Cull ambient population and traffic density. Fewer NPC vehicles means dramatically less to synchronise.
- Use routing buckets for interiors, instanced content and admin areas. Players in a different bucket cost you nothing to sync.
- Delete entities you create. Every abandoned prop stays in the pool.
- Watch entity counts with the server-side entity list, not by feel.
Streamed assets: crashes, memory and load times
Custom cars, MLOs, clothing and weapon packs do not cost server milliseconds — they cost client memory and join time. When new players crash on connect or the game stutters entering a custom interior, this is nearly always the cause.
- Every streamed vehicle costs client VRAM whether anyone drives it or not. Two hundred add-on cars is a real memory budget.
- Compress your YTD textures. A 4096×4096 texture on a wheel rim is a mistake someone made, not a requirement.
- Watch for duplicate assets across resources — two MLOs shipping the same props will conflict, and one of them will win unpredictably.
- Raise pool sizes deliberately with sv_poolSizesIncrease when you genuinely need them, and only for the pool that is actually exhausted.
- Keep the total stream size in mind: everything a joining player must download before they spawn is your first-impression budget.
A repeatable optimisation workflow
- Reproduce the problem with a number. "Server ms goes from 4 to 22 at 60 players" is actionable; "it lags" is not.
- Measure before you change anything. Screenshot resmon and the server console.
- Change one thing.
- Measure again under the same conditions.
- Write down what worked. Six months later you will not remember.
Most servers can cut their client frame cost in half in a single afternoon with this loop, without removing a single feature — just by fixing the five worst loops and adding four indexes.
Frequently asked questions
What is a good server ms for FiveM?
Under 5 ms at your normal player count is healthy. 5–15 ms is workable but worth investigating. Sustained above 20 ms is where players start feeling desync and rubber-banding.
Why does my FiveM server lag with many players?
Usually per-player server loops or database queries that scale with player count, combined with broadcast events sent to everyone. Entity pressure from ambient traffic and un-cleaned spawned vehicles is the other common cause.
Does more RAM fix FiveM lag?
Rarely. FiveM is bound by single-thread CPU speed, not memory. RAM matters for the database and for holding streamed assets, but adding it to a server with a 30 ms tick changes nothing.
How do I find which resource is causing lag?
Run resmon 1 on a client for client-side cost, and bisect by stopping half the resources at a time for server-side cost. Combine that with oxmysql's slow-query log to catch database stalls.
Need this built, not just explained?
Ten years of FiveM work, from Lua to NUI
Custom resources, React NUI, ESX / QBCore / Qbox integration, OneSync performance audits and security reviews — plus the websites and SEO around your server brand.
Related guides
- Getting startedHow to make a FiveM serverFrom an empty VPS to a public GTA V roleplay server: artifacts, licence key, server.cfg, framework, database, OneSync and the launch checklist.
- FrameworksESX vs QBCore vs QboxData models, inventories, script ecosystems, performance and hiring cost — the real differences between the three FiveM frameworks.
- Assets & UIFiveM asset streamingStream folders, add-on vehicles, MLOs, texture budgets and pool sizes — and how to find the asset that is crashing your players.
- EngineeringFiveM server securityEvent validation, trust boundaries, backdoored resources, txAdmin hardening and what anti-cheat can and cannot do.