How to Optimize Your FiveM Server for 300+ Players
Running a FiveM server at 300+ concurrent players isn't just about throwing hardware at the problem. It requires deliberate optimization at every layer — from how your Lua scripts execute to how your database handles concurrent queries. Here's what we've learned running HPRP at scale.
1. Eliminate Busy-Wait Loops
This is the single biggest performance killer in FiveM resources. A while true do Wait(0) end loop that checks distance to a location runs every single frame for every player. At 300 players, that's 300 instances of that loop, each consuming a tick.
The fix: use ox_lib's points and zones instead. These use spatial partitioning and only trigger callbacks when a player enters or exits a defined area.
-- BAD: Runs every frame for every player
CreateThread(function()
while true do
local pos = GetEntityCoords(PlayerPedId())
if #(pos - targetCoords) < 3.0 then
-- do something
end
Wait(0)
end
end)
-- GOOD: Only fires on enter/exit
local point = lib.points.new({
coords = targetCoords,
distance = 3.0,
})
function point:onEnter()
-- do something
end
2. Use Modern Natives
Small things add up at scale. Using deprecated or slower native functions across hundreds of resources creates measurable overhead.
- Use
PlayerPedId()instead ofGetPlayerPed(-1) - Use
#(a - b)for distance instead ofGetDistanceBetweenCoords() - Use backtick hashes (
`adder`) instead ofGetHashKey('adder') - Use
vector3()types for coordinate math
3. Optimize Database Queries
Database calls are the most common source of server hitches at high player counts. Every query that isn't parameterized or batched is a potential bottleneck.
- Always use parameterized queries —
MySQL.query.await('SELECT * FROM users WHERE id = ?', {playerId}) - Never concatenate SQL strings — it's both a security risk and slower
- Index your columns — if you query by
citizenid, make sure it's indexed - Batch operations — combine multiple inserts/updates into single queries where possible
- Use async queries when you don't need the result immediately
4. Minimize Event Traffic
Server events are broadcast to all connected players by default. At 300 players, a poorly scoped event creates 300x the network traffic.
- Use
TriggerClientEventwith a specificsourceinstead ofTriggerClientEventto all - Only send data that the client actually needs
- Debounce frequent events — don't fire on every frame
- Consider state bags for values that change infrequently
5. Audit Your Resources with Resmon
FiveM's built-in resource monitor (resmon 1 in the F8 console) is your best friend. Check it regularly and investigate any resource consistently above 0.5ms.
Common offenders:
- Resources with
Wait(0)loops — rewrite with ox_lib zones - Resources that poll for key presses every frame — use
RegisterKeyMappinginstead - Resources that draw 3D text — use ox_target or NUI
- Resources with unoptimized thread counts — consolidate where possible
6. Server Hardware Matters (But Not as Much as Code)
Good hardware helps, but it can't fix bad code. A well-optimized server on modest hardware will outperform a poorly-optimized server on expensive hardware every time.
That said, for 300+ players you want:
- High single-core CPU performance (FiveM is largely single-threaded)
- Fast NVMe storage for the database
- Sufficient RAM (16GB+ recommended)
- Good network connectivity with low latency
7. Framework Abstraction
If you're planning to migrate from QBCore to QBox (or want to keep your options open), abstract your framework calls. Don't scatter QBCore:GetPlayer() calls throughout every resource. Instead, create a shared bridge module that wraps framework calls.
This isn't just about future-proofing — it also makes your code cleaner and easier to maintain, which directly impacts your ability to optimize.
Every script we release at Cryptic Scripts uses runtime framework detection. No hard dependencies on qb-core, no breaking when you upgrade. This is how all FiveM scripts should be built.
The Bottom Line
Optimizing for 300+ players is about discipline, not magic. Replace busy-wait loops with event-driven patterns. Use modern natives. Parameterize your queries. Scope your events. Audit with resmon. The tools exist — you just have to use them consistently.
If you want scripts that already follow these principles, check out our Cryptic Scoreboard — built from the ground up for high player counts with a 0ms resmon target.
Need scripts built for scale?
Every Cryptic Scripts resource is battle-tested on a 300+ player server before release.