PiTyUs.Hire me

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.

Updated 13 min readBy PiTyUs · FiveM developer

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.

SymptomAlmost certainlyWhere to look
Low FPS for everyone, everywhereClient-side resource costresmon 1 on a client
Server ms climbs as players joinServer-side per-tick work or DB queriesServer console, txAdmin performance chart
Rubber-banding, desync, vehicles teleportingNetwork or OneSync entity pressurePlayer ping, entity counts, routing buckets
Stutter when driving into an areaStreamed assets loadingYour stream folder, MLO and vehicle packs
Freezes for a second, then resumesA blocking database queryoxmysql 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.

The pattern that ruins serverslua
-- 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.

Adaptive sleep — the minimum acceptable versionlua
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)
Better — let a points system do itlua
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.

  1. Synchronous database queries. Any query that is awaited inside a hot path serialises the whole server behind your slowest SQL.
  2. Events fired at every player. `TriggerClientEvent(name, -1, payload)` with a large payload, several times a second, is a broadcast storm.
  3. Per-player server loops. A thread iterating every online player every tick scales quadratically with your player count.
  4. Unbounded entity creation. Spawning vehicles or props server-side without cleanup fills the entity pool until sync degrades.
Batch instead of broadcastinglua
-- 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.

The indexes almost every server is missingsql
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

  1. Reproduce the problem with a number. "Server ms goes from 4 to 22 at 60 players" is actionable; "it lags" is not.
  2. Measure before you change anything. Screenshot resmon and the server console.
  3. Change one thing.
  4. Measure again under the same conditions.
  5. 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