Find the resource that makes your FiveM server lag
Server stalls or low FPS? How to tell them apart, read FXServer’s profiler, count each resource’s commands and find the script behind the lag.
By Termi
- Performance
- Debugging
- Lua
On this page
- Two kinds of lag
- Read the server profiler
- Measure a player’s FPS
- The cause neither tool shows
- The usual suspects
- Common questions
- What does “server thread hitch warning” mean in FiveM?
- Why does my FiveM server hitch every few seconds?
- How do I find which resource is lagging my FiveM server?
- What does resmon show in FiveM?
- What does oxmysql’s “took … ms to execute a query” warning mean?
- The same hunt in one command
“The server lags” is two different problems. Sometimes the server thread stalls and everyone stutters at once; sometimes some players lose FPS while the server is fine. This guide shows how to tell them apart, how to read FXServer’s own profiler, and how to find the cause that hides from both: a resource that registers thousands of commands. At the end, the same hunt in one command.
Two kinds of lag
They need different tools, so first check which one you have: are there hitch warnings, and does everyone feel it or only some players?
Read the server profiler
FXServer ships with a profiler that times every resource, thread and event handler. In the server console:
profiler record start
profiler record stop
profiler saveJSON lag.jsonStart it while the server lags, stop it after ten seconds or so, and save the recording. lag.json is a Chrome trace: open DevTools in Chrome, go to Performance and load the file. Each server frame is a Resource Manager Tick, and inside it you find every resource’s tick (name), its threads as thread @resource/file.lua[from..to], its event handlers and its exports.
Look for frames that take far longer than the rest and see which resource fills them. The line range tells you which CreateThread or handler it was.
A resource cannot start the profiler by itself. ExecuteCommand('profiler record start') from a script is refused unless server.cfg allows it: add_ace resource.<name> command.profiler allow, plus the same line for command.record and command.saveJSON.
Measure a player’s FPS
On the player’s side, F8 has two tools. resmon 1 shows every resource’s CPU time and memory live, which is enough when one resource clearly stands out. For a closer look, the same profiler runs in the game:
resmon 1
profiler record 500
profiler saveJSON lag.jsonThat records 500 frames. Load the file in DevTools the same way and look for client threads that run every frame. A Wait(0) loop that does real work in each frame, like measuring the distance to hundreds of markers, is the usual first suspect.
The cause neither tool shows
One kind of lag hides from both. A server adds an emote pack and soon after it starts to lag. The profiler shows no heavy thread, and searching the pack for RegisterCommand finds a handful of calls.
The calls sit in a loop, behind a local alias, one for every emote:
local register = RegisterCommand
for _, emote in ipairs(Emotes) do
register(emote.command, function() PlayEmote(emote) end, false)
TriggerEvent('chat:addSuggestion', '/' .. emote.command, emote.label)
endWith two thousand emotes, that is two thousand commands and two thousand chat suggestions in every player’s game. A text search cannot count them, but the game can: GetRegisteredCommands() returns every command with the resource that registered it.
local counts = {}
for _, command in ipairs(GetRegisteredCommands()) do
counts[command.resource] = (counts[command.resource] or 0) + 1
end
for resource, count in pairs(counts) do
if count > 100 then print(resource, count) end
endRun it as a client script and as a server script. A resource with hundreds of commands is worth a look. One with thousands almost always registers a command for every emote or item, where one command with an argument would do.
The usual suspects
Common questions
What does “server thread hitch warning” mean in FiveM?
FXServer’s main thread is meant to tick 20 times a second, once every 50 ms. When more than 150 ms pass between two ticks, the console prints server thread hitch warning: timer interval of 212 milliseconds, and the number is how long that gap lasted. Nothing else on the server thread runs during the gap, so every player feels it at the same moment. Something on the server held the thread too long, usually one resource doing a lot of work in a single frame.
Why does my FiveM server hitch every few seconds?
A hitch on a regular beat usually comes from a server thread that sleeps a few seconds with Wait and then does heavy work in one go, such as walking every player, vehicle or job point. The server profiler shows which thread it is: the long frames name the resource and the lines of the thread, for example thread @jobs_garbage/server/main.lua[88..131].
How do I find which resource is lagging my FiveM server?
Use FXServer’s built-in profiler. Run profiler record start in the server console, profiler record stop about ten seconds later, then profiler saveJSON lag.json, and load the file in the Performance tab of Chrome DevTools. Every resource’s tick, thread and event handler shows up with its time, so the one behind a long frame stands out. If only some players lag, have them type resmon 1 in F8 instead to see each client resource’s CPU time live.
What does resmon show in FiveM?
resmon 1, typed in the F8 console, opens the resource monitor: a live list of every client resource with the CPU time it takes each frame and the memory it holds. It measures only the game of the player who opened it, so it explains that player’s FPS and says nothing about a server hitch.
What does oxmysql’s “took … ms to execute a query” warning mean?
oxmysql prints a line like [10.11.6-MariaDB] jobs_garbage took 312.4512ms to execute a query!, followed by the query, whenever a query runs longer than the mysql_slow_query_warning convar, 200 ms by default. The resource name tells you which script sent it. The query runs asynchronously, so it does not stall the server thread by itself, but the script waiting for the result is late, and players see a garage or an inventory that opens slowly. A missing index on the column in the WHERE clause is a common cause; put EXPLAIN in front of the query to see whether MySQL uses one.
The same hunt in one command
FiveDock’s agent does all of this for you. With the three add_ace lines in server.cfg, fivedock perf records the profiler for ten seconds, counts every resource’s commands on the server and in three players’ games, and asks every player for their FPS:
my-city profiled 10 s · 214 players online
server thread late by 3.8 ms on average, 212 ms at worst in the last minute
Server 198 ticks, the slowest took 212 ms
resource total per tick worst tick
jobs_garbage 1,236 ms 6.2 ms 205 ms
hud 412 ms 2.1 ms 6.3 ms
oxmysql 96 ms 0.5 ms 14 ms
emotes 12 ms 0.1 ms 0.4 ms
Where exactly the threads, events and exports that took longest
jobs_garbage 611 ms thread · server/main.lua:88-131 · 3× · worst 203 ms
jobs_garbage 598 ms thread · server/main.lua:12-40 · 198× · worst 6.1 ms
hud 380 ms event hud:sync · server.lua:52-77 · 1,420× · worst 4.2 ms
oxmysql 88 ms export · dist/build.js:310 · 402× · worst 14 ms
Commands on the server, and in the game of 3 sampled players
resource server players
emotes – 2,034
admin_menu 61 12
hud 9 4
total 248 2,310
A command per emote or item adds up fast. Check whether that resource needs them all.The garbage job’s thread at server/main.lua:88-131 holds the server up for about 200 ms every few seconds, and the emote pack has 2,034 commands in the players’ game. When the server thread stalls while nobody is watching, the agent records the next ten seconds by itself, so the next report already says what happened.
Find what lags covers the setup and every part of the report.