On this page
- How the two threads differ
- Confirm which thread before you change anything
- 1. Too many networked entities
- 2. Broadcast events and replicated state bags
- 3. Player count against what the server is carrying
- 4. OneSync mode and population settings
- 5. Server list queries hitting your public endpoint
- 6. Hostile traffic
- Telling an attack from organic load
- If it is an attack
- What does not fix this
- Related
network thread hitch warning - frame time of 210 milliseconds means the
server's network thread was blocked for 210 milliseconds. That is not the
thread your scripts run on. It is the thread that moves packets between your
server and every connected client and that drives player and entity
synchronisation, so a stall there shows up as rubber-banding, entities snapping
into place, and joins timing out, while the server itself keeps ticking.
That difference is the whole point of this page. A network thread hitch is a volume problem: how many entities are being synced, to how many players, and how much traffic is arriving. It is very rarely a badly written loop, and the fixes that work for the other warning do nothing here.
How the two threads differ
| Main thread | Network thread | |
|---|---|---|
| Runs | Every server-side resource tick, event handler, and command | Packet send and receive, player and entity sync, connection handling |
| Stalls because of | Code that does not yield, or something it waits on | The volume of sync and packets it has to get through |
| Players report | Commands, menus, and chat answer late; everything pauses together | Rubber-banding, entities teleporting, players timing out, joins failing |
| Fixed by | Editing the resource that blocks it | Reducing entities, sync scope, or traffic |
| Warning text | server thread hitch warning | network thread hitch warning |
If your console says server thread, this is the wrong page: go to
server thread hitch warnings.
Some artifact builds run a separate sync thread as well, and txAdmin's performance chart lets you select it. Read it alongside the network thread, since the causes below drive both.
Confirm which thread before you change anything
Read the warning text, exactly
The first word is the answer. Copy the line out of the console rather than working from memory or from what someone told you it said.
Look at the per-thread chart
Open txAdmin's dashboard performance chart and switch between the threads. You are looking for whether one of them is spiking on its own or whether they spike together.
If both threads hitch together, look at the machine
A host that stalls stalls every thread at once. Check for disk wait, steal time, and swapping before doing anything else, using the checks in the machine underneath the server.
Note whether player count was rising, flat, or falling
This one line of information sorts most of the causes below, and it separates organic load from an attack better than any other single check.
1. Too many networked entities
The most common cause on an established server, because entity counts only ever go one way without deliberate cleanup.
Symptom. Hitching gets steadily worse through a session and disappears after a restart. It is worse when players cluster, at a city centre, a car meet, or a scheduled in-game event, than when the same number of people are spread out.
Confirm it. With OneSync on, count what exists, once when the server is fresh and again after a busy evening:
print(('vehicles %d, peds %d, objects %d'):format(
#GetAllVehicles(), #GetAllPeds(), #GetAllObjects()))A count that only climbs is your answer. Every networked entity has to be tracked and synced to the players near it, so the work is not the number of entities, it is entities multiplied by the players who can see them. That is why clustering hurts so much more than raw player count.
Fix it. Delete abandoned vehicles and orphaned props on a schedule, in
bounded batches. Find the resources that spawn and never delete. Then stop
clients adding to the pile: sv_entityLockdown in your server.cfg set to
relaxed or strict prevents clients creating entities the server did not
authorise, which closes both a performance hole and a cheating one. See
server.cfg.
2. Broadcast events and replicated state bags
Symptom. Hitching that started after a resource was added or updated, and that scales with player count. Often a resource whose main-thread cost in the profiler looks trivial, which is exactly why people rule it out.
Confirm it. Find the broadcast patterns:
grep -rn "TriggerClientEvent(" resources/ | grep -- "-1" | head -40
grep -rn "state:set(" resources/ | head -40For each hit, ask two questions: how often does this run, and how many recipients
does it have. A payload sent to one player once is nothing. The same payload sent
to -1 on a one-second timer, with 80 players connected, is 80 sends a second
forever.
State bags deserve their own look. Entity(vehicle).state:set('key', value, true)
replicates the value to clients. Doing that inside a tick replicates it every
frame, for every entity the code touches, and it is one of the easiest ways to
saturate the network thread with data nobody reads.
Fix it.
- Send to the players who need it, not to
-1. - Use
TriggerLatentClientEventfor anything large. It takes a byte rate and spreads the payload over time instead of pushing it all at once. - Pass
falseas the replication argument on state bags clients do not read. - Set state on change, not on a timer, and never every frame.
3. Player count against what the server is carrying
Symptom. It starts at roughly the same player count every night. Below that number the server is fine, above it, it hitches consistently.
Confirm it. Line the hitches up against the player graph in txAdmin. If the onset is reproducible at a player count, the load scales with players and this is the cause rather than a coincidence.
Worth understanding before you react: sync work does not grow in a straight line with players. Each player has to be told about the entities and players near them, so 60 players in one place is a great deal more work than 60 players spread across the map. This is why a server that is comfortable at 60 every day falls over during an in-game event with the same 60 people.
Fix it. sv_maxclients is a ceiling, not a target. Set it to what your server
actually carries with the resources you run, and raise it when you have measured
headroom. Raising it because you would like more players adds no capacity, it
only lets more people in before the hitching starts, and what you get is clamped
to what your OneSync mode and licence tier allow anyway. Reducing the reasons for
players to pile into one place, or reducing entity density at events, is the
other half of the answer.
4. OneSync mode and population settings
Symptom. Hitching on a server that has grown past its original size, or one that was configured years ago and never revisited.
Confirm it. Type each convar name on its own in the server console. It prints the value currently in force, which is the only reading that counts, since a config line can be overridden later in the file or by another resource:
onesync
onesync_population
sv_maxclientsFix it. Three things to get right:
onesynctakeson,legacy, oroff.onis the mode that culls entities by distance, so each client only receives what is near it and sync work stops growing with the whole world.legacydoes not scale the same way, andoffputs you on the base game network model with a 32 client limit. What ceiling you get withondepends on your licence tier, which server.cfg covers.onesync_populationdecides whether ambient traffic and pedestrians are networked. Turning it off removes an entire class of entities from sync. On a roleplay server where ambient traffic is scenery, this is one of the few settings that changes entity counts by a large factor. Measure your counts before and after with the snippet above rather than assuming.- Entity culling and lockdown settings belong to the same conversation. See server.cfg for the full set and where they go in the file.
5. Server list queries hitting your public endpoint
Your server answers /info.json, /players.json, and /dynamic.json on its TCP
port, unauthenticated, to anybody who asks. That is how the server list, and
every third-party server tracker, reads your details.
Symptom. Inbound and outbound traffic that does not match your player count. Load that continues when the server is nearly empty.
Confirm it. Measure what one of those replies costs you. From the server itself:
curl -s -o /dev/null -w '%{size_download} bytes\n' http://127.0.0.1:30120/info.jsonCompare that with the size of the request that triggered it, which is a few dozen
bytes. /info.json carries your full resource list and your server icon embedded
in it, so the reply is very much larger than the request. That ratio is the whole
attraction for anyone using it as an amplifier, and it is also why a tracker
polling you every few seconds is not free.
Fix it. You cannot turn the endpoint off and stay listed, so reduce what it
costs: keep the server icon to the small square PNG the server list expects, and
keep the resource list from being longer than it needs to be. Both are covered in
server.cfg. As a diagnostic only, setting sv_master1 to an empty value
unlists the server, so if the load disappears you have confirmed where it came
from. That is a test, not a fix, since players cannot find an unlisted server.
6. Hostile traffic
Symptom. The network thread hitches while player count is flat or falling. Players time out in groups. New players cannot join while people already in the game are fine, or everyone drops at once. It starts and stops abruptly rather than building with your evening.
Read the next section before concluding this, because organic load looks similar from the console and the two need completely different responses.
Telling an attack from organic load
Four checks, and you want more than one of them to agree.
1. Does the load track player count? Organic load rises and falls with players. Attack traffic does not care how many people are on your server. Load that climbs while your player count falls is the single strongest signal you can get.
2. Does the bandwidth divide sensibly by player count? Measure inbound traffic during a hitching period. On Linux:
sar -n DEV 1 10sar comes from the sysstat package, which is apt install sysstat on Debian
and Ubuntu and dnf install sysstat on AlmaLinux and Rocky. rxpck/s is packets
received per second and rxkB/s is inbound kilobytes per second, per interface. Divide by your connected player count and compare with the
same figure taken on a normal night at the same player count. Organic traffic per
player is roughly stable. A figure several times your normal per-player rate is
not organic. On Windows, Get-NetAdapterStatistics in PowerShell gives you the
counters to do the same arithmetic.
3. Where is it coming from? Take a sample of packets on your game port and count the sources:
sudo tcpdump -ni eth0 -c 5000 udp port 30120 2>/dev/null \
| awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn | head -20Replace eth0 with your interface, and 30120 with your port if you changed it.
tcpdump is apt install tcpdump on Debian and Ubuntu, dnf install tcpdump on
AlmaLinux and Rocky.
Then compare the top talkers with the endpoints of your actual connected players,
which txAdmin lists for you. Heavy senders that are not on your player list are
not playing your server. Traffic arriving on a port you run nothing on is
conclusive on its own.
4. Does it stop when you are empty? Traffic that continues at 4am with nobody connected is not your players.
If it is an attack
Filtering happens at the network edge, before traffic reaches your server, so what to do about it is not a change on the machine. Work through the protection docs:
- Game presets writes the rules for FiveM, which need both a TCP and a UDP rule on your game port. This is the fastest starting point if you have no rules at all.
- Port rules covers doing it by hand, and the fact that rules are per protocol, so a game needing TCP and UDP needs two of them.
- Rate limits covers sizing a ceiling from your own measured peak rather than from an attack. This is where the packets per second you measured in check 2 above become useful.
- When players cannot connect is where to go if tightening the limits starts cutting real players, which is the usual next problem.
One thing to be clear about: traffic that is dropped at the edge never reaches your server at all. So a network thread hitch that persists while an attack is being filtered is either the portion that is being allowed through, because it resembles legitimate game traffic closely enough, or it is one of the causes higher up this page happening at the same time. Check both rather than assuming the attack explains it.
If you have measured evidence of traffic reaching your server that should not be, open a ticket with the port and protocol, the sample of source addresses you collected, the times, and the packets per second you measured. Those are what turn a report into an investigation.
What does not fix this
- Adding
Citizen.Waitto resources. Your Lua does not run on this thread. - More CPU cores. Nothing here is short of cores.
- A scheduled restart. It resets entity counts, which hides the most common cause of this warning for a few hours at a time and guarantees you never find it.
- Raising
sv_maxclients. It is a ceiling, and raising it during a hitch adds load.
Related
The other warning. Different thread, different causes, different fixes.
Optimising a FiveM serverresmon, the profiler, and how to measure before you change anything.
Game presetsThe rules a FiveM preset writes for you, and what it turns on.
Rate limitsSizing a packet rate ceiling from your own traffic.