DEEP-DIVEPERFORMANCEOPTIMIZATIONLUARESMONSERVER OPTIMIZATIONLAG FIXFPS FIXSERVER PERFORMANCETROUBLESHOOTING February 26, 2026 · 5 min read

FiveM Performance Guide — Fix Lag With Resmon

Your server is lagging. Players are complaining. You’ve got 40 people online and the server feels like it’s running through mud.

I deal with this constantly — both in my own scripts and when helping server owners debug their setups. Nine times out of ten, the problem is one or two badly written scripts eating resources they don’t need. Here’s how to find them and fix them.

Resmon: Your Best Friend

Type resmon 1 in your server console (or F8 in-game for client-side). You’ll see a live overlay showing how many milliseconds each resource is burning per frame.

Here’s how to read it:

  • Under 0.1ms per script — totally fine, don’t touch it
  • 0.1ms to 0.5ms — worth looking at if you’re having issues, but probably not the culprit
  • 0.5ms to 1.0ms — this is a problem. Something in this script is running too hot
  • Over 1.0ms — drop everything and fix this. A single script above 1ms will make your whole server feel sluggish

For reference, a 64-tick server has about 15.6ms of total frame budget. If one script eats 2ms of that, it’s consuming 13% of your server’s entire capacity by itself.

Your total server frame time should stay under 8ms. If it’s consistently above that, you’re going to have desync and rubberbanding.

The Three Scripts That Are Probably Killing Your Server

I’m not guessing here. After years of helping people optimize, the same patterns show up over and over.

1. Scripts With Wait(0) Everywhere

This is far and away the most common problem. Wait(0) means “run this code every single frame.” That’s 60 times per second on most clients. The vast majority of scripts don’t need to run that often.

-- This runs 60 times per second. Why?
Citizen.CreateThread(function()
    while true do
        Wait(0)
        local pos = GetEntityCoords(PlayerPedId())
        -- check if player is near a shop
    end
end)

The fix is dead simple — use a longer wait and only drop to Wait(0) when the player is actually close to something:

Citizen.CreateThread(function()
    while true do
        local sleep = 500  -- check twice per second normally
        local pos = GetEntityCoords(PlayerPedId())
        local dist = #(pos - shopCoords)

        if dist < 10.0 then
            sleep = 0  -- nearby, need per-frame checks for interaction
            -- draw marker, handle prompt
        end

        Wait(sleep)
    end
end)

I’ve seen this single change take a script from 0.8ms to 0.01ms. That’s not an exaggeration.

2. Database-Happy Scripts

Some scripts hit the database like it owes them money. Every time a player opens a menu, picks up an item, or looks at their phone — another SQL query.

-- Every. Single. Time. Someone opens inventory.
RegisterNetEvent('inventory:open')
AddEventHandler('inventory:open', function()
    local items = MySQL.query.await(
        'SELECT * FROM user_inventory WHERE identifier = ?',
        {identifier}
    )
end)

The fix: cache the data when the player loads in and update the cache when things change. Don’t re-query data you already have.

local cache = {}

AddEventHandler('esx:playerLoaded', function(playerId, xPlayer)
    cache[playerId] = MySQL.query.await(
        'SELECT * FROM user_inventory WHERE identifier = ?',
        {xPlayer.identifier}
    )
end)

On a server with 32 players, cutting unnecessary queries can save hundreds of database calls per minute. Your database will thank you.

3. Native Spam

GTA natives vary wildly in cost. Some are basically free (GetEntityCoords), others are expensive (GetClosestVehicle, GetGamePool). The expensive ones shouldn’t be called every frame:

-- Stagger expensive calls into a separate thread
Citizen.CreateThread(function()
    while true do
        local vehicle = GetVehiclePedIsIn(PlayerPedId(), false)
        if vehicle ~= 0 then
            cachedHealth = GetEntityHealth(vehicle)
            cachedPlate = GetVehicleNumberPlateText(vehicle)
        end
        Wait(1000)  -- once per second is plenty for these
    end
end)

Quick Fixes You Can Do Right Now

You don’t need to rewrite anything to get some quick wins:

  1. Delete scripts you’re not using. Every ensure in your cfg has a cost, even if the script seems idle. If you’re not using it, remove the ensure line.

  2. Update your framework. ESX Legacy, QBCore, and Qbox all ship performance patches regularly. Running a version from 6 months ago means you’re missing free performance improvements.

  3. Install ox_lib. It replaces a lot of common patterns (notifications, context menus, callbacks) with optimized versions. If scripts you’re using support ox_lib, enable it.

  4. Check your tick rate. Running 64 tick when 48 tick would be fine for your RP server is wasting ~33% more server CPU for no visible benefit to players.

  5. Look at your streaming assets. Oversized YTD files (clothing, vehicle textures) don’t show up in resmon but absolutely murder client performance. Use a tool like OpenIV to check texture sizes — anything over 2MB per YTD is suspect.

How We Handle This at YBN

Every script we ship targets under 0.05ms idle on both client and server. We use adaptive sleep patterns, proper caching, and we test on populated servers before release — not just on a local machine with one player.

If your server’s struggling and you want help figuring out what’s eating performance, come by our Discord. I help server owners track down performance issues regularly, even when the problem scripts aren’t ours.

Looking for scripts built with performance in mind? Check out the store.

Keep Reading

YBN
YBN Scripts
FiveM script developer at YBN. Building premium ESX, QBCore & Qbox resources.

Related Posts

Need scripts for your server?

Check out our premium FiveM resources — ESX, QBCore & Qbox supported.

Browse Premium Scripts → Free Scripts →