← Back to Tutorials
Advanced ~18 min read UE 5.5 / 5.6

Dedicated Server Performance

Server frame budget at 30 Hz is 33.3 ms; at 60 Hz it's 16.6 ms shared across game tick, AI, physics, and replication. The headline is replication, but the interesting wins live in the gap between "default NetUpdateFrequency=100" and "Fortnite ships 100 players with ~50,000 replicated actors per match." This tutorial covers the server build target, the dormancy/push-model/Iris ladder, AI throttling, and the per-class dormancy bugs that quietly decimate server CPU.

1

Why server perf is its own discipline

A dedicated server has no GPU, no audio, no UI — and pays for none of those budgets. What it does pay for is replication. On a server, the entire frame budget is devoted to game tick + AI + physics + replication, and replication can take more than half of it. Frame loss on a server cascades to every connected client as input lag and prediction-correction snaps.

Reference scale: Fortnite Battle Royale runs 100 connected players with roughly 50,000 replicated actors per match (per Replication Graph docs). That's the canonical "this is what made Replication Graph necessary" stat.

2

The Server target & no-graphics path

A proper dedicated server uses a server-typed target, not just runtime flags:

YourGameServer.Target.cs
using UnrealBuildTool;
using System.Collections.Generic;

public class YourGameServerTarget : TargetRules
{
    public YourGameServerTarget(TargetInfo Target) : base(Target)
    {
        Type = TargetType.Server;
        DefaultBuildSettings = BuildSettingsVersion.V5;
        IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
        ExtraModuleNames.AddRange(new string[] { "YourGame" });
    }
}
Server launch flags
YourGameServer.exe MyMap -log -nullrhi -unattended -nosound -NoVerifyGC

Type=TargetType.Server is what strips the editor-only modules and render-thread setup at compile time. The runtime -nullrhi flag alone is not enough — without the typed target you ship a render thread that does nothing but eat memory.

⚠
Don't accidentally start a listen server ?listen on a server command line silently makes it a listen server with a hidden local player. Two replication paths now run; CPU near-doubles. The dedicated-server flag is plain -server; ?listen belongs only on listen-server / single-host configurations.
3

Server-only cooks

A server-only cook strips content the server doesn't need: textures over 64×64, lightmaps, audio, particle render data. The build pipeline:

UAT command
RunUAT BuildCookRun -project=YourGame.uproject ^
    -server -serverplatform=Linux -noclient ^
    -cook -stage -archive ^
    -archivedirectory=Builds/Linux_Server

-noclient + -server tell UAT to skip the client-side cook entirely. Per-asset, you can mark cook rules as NotForServer in primary asset types or per-class. The server cook is typically 50–70% smaller than the client cook on shipped multiplayer titles.

4

Tick rate strategy — 30 vs 60 Hz

NetServerMaxTickRate defaults to 30 Hz. Configured in DefaultEngine.ini under [/Script/Engine.NetDriver] — not a runtime CVar.

DefaultEngine.ini
[/Script/Engine.NetDriver]
NetServerMaxTickRate=30
; 60 Hz roughly doubles per-frame replication CPU.
; Worth it on competitive shooters; not for BR/MMO.

Picking 30 vs 60 by genre is the right starting point: BR/MMO at 30, FPS/fighter at 60. Doubling tick rate does not halve latency — it halves the variance of the worst-case packet arrival, which matters for projectile prediction and hit reg, but the mean trip is dominated by network distance.

5

The replication budget & mitigation ladder

"The frame is for replication" is the right mental model on a busy server. Mitigations in priority order:

  1. Dormancy — DORM_DormantAll for static interactables, DORM_Initial for spawn-once objects. Per Epic, "potentially saves multiple milliseconds of server CPU time per frame."
  2. Push model — net.IsPushModelEnabled=1, bIsPushBased=true on UPROPERTY, plus bWithPushModel=true in the Target.cs. Skips dirty-check polling on properties marked push-model. Community-measured: 4-client test dropped NetBroadcastTickTime from 167.4 ms to 76.5 ms (>2× speedup).
  3. Adaptive net update frequency — net.UseAdaptiveNetUpdateFrequency=1; engine throttles per-actor between NetUpdateFrequency and MinNetUpdateFrequency.
  4. NetCullDistanceSquared — default 225,000,000 cm² ~ 15,000 cm (150 m) relevancy radius. Lower for actors that don't matter at distance.
  5. Replication Graph — spatial-hash relevancy; what Fortnite ships.
  6. Iris — the new replication system, experimental in 5.5, more mature in 5.6+.
📝
Iris vs default at 100 players BorMor's published benchmarks (UE 5.4+, methodology mirrors Epic's internal numbers): default replication NetBroadcastTickTime 66.2 ms / server 10–15 fps; Iris NetBroadcastTickTime 45.8 ms / server 15–20 fps. Patched-Iris dropped broadcast to 29.5 ms (~55% below default). Source: Iris: 100 Players in One Place.
6

Dormancy in practice

Dormancy is the single highest-leverage server optimization. The states:

  • DORM_Awake — default; actor replicates every server tick.
  • DORM_DormantAll — replicates only when FlushNetDormancy() is called or property dirty.
  • DORM_Initial — replicates exactly once at relevancy and never again unless flushed. Use for spawn-once props.

Common dormancy bugs documented in the community:

  • Blueprint-replicated UPROPERTYs ignore dormancy. Actor stays DORM_Initial, BP variables still send (per Vorixo's DORM_Initial deep dive).
  • ForceNetUpdate silently no-ops when NetDriver==nullptr, e.g., before UWorld::Listen(). Use FlushNetDormancy directly.
  • Mutating a replicated property without a manual flush on a dormant actor — the property change replicates but only because of the implicit dirty-flag; some BP-mutated paths don't trigger it.
7

Replication Graph vs Iris

Replication Graph is mature, Fortnite-proven, the right call for any title with serious player counts on UE 5.0–5.4. Spatial 2D grid (UReplicationGraphNode_GridSpatialization2D), AlwaysRelevantNode, ConnectionGraphNode, FastSharedReplicationPath. Enable by setting ReplicationDriverClassName in DefaultEngine.ini to your custom UReplicationGraph-derived class.

Iris is the new system, default-compiled since 5.1, experimental in 5.5, target for 5.6+. Replaces the legacy NetDriver replication path with NetTokens, ReplicationFragments, and a fundamentally different bandwidth model. Enable per-world via net.Iris.UseIrisReplication=1 and opt new actors into the SubObject path with net.SubObjects.DefaultUseSubObjectReplicationList=1.

Don't migrate to Iris yet for a shipping title. Verify on a soak test first; Iris buffer growth is monotonic per Epic's documented gotcha — once peak replicated-object count blows the buffer, it stays expanded for the lifetime of the server process.

8

AI throttling on the server

The server runs every NPC's AI; clients run almost none. AI is therefore proportionally a much bigger server-CPU concern than it is on a single-player shipped title.

The mitigation stack:

  • Significance Manager — Epic's plugin for tick-rate decimation by significance (typically distance + screen-space). Tom Looman's optimization talk frames this as the "tick budget" pattern.
  • Mass Entity — for >500 AI, the data-oriented Mass framework instead of one Actor per entity. Far cheaper per agent.
  • BehaviorTree decorator throttling — gate expensive selectors on cheap predicates that don't run every tick.
  • AI Perception sense interval — default 0.2 s. Don't drop without measuring; raising to 0.5 s on AI that don't need fast reactions is a measurable win.
9

Networked physics on the server

Physics on the server is a frequent source of regression. The pitfalls:

  • Variable game tick + fixed physics tick mismatch is the #1 desync source. If you need rewind/resim networking, switch to Async Physics Tick + Fixed-Tick mode (covered in the Chaos Physics tutorial).
  • Replicating physics state for every body is expensive in bandwidth. Replicate the spawn event, simulate locally; only replicate authoritative transforms for hero objects.
  • Substepping is mutually exclusive with Async Physics Tick. Pick one based on your network model.
10

Server memory & profiling

Typical 4–8 GB headless target on cloud-hosted dedicated servers. The cost is dominated by world content + per-connection state (replication subobject lists, Iris buffers, dormancy bookkeeping).

Profiling the server:

  • stat net — NetTickTime, NetBroadcastTickTime, replicated property counts. Primary triage view.
  • stat game — gameplay tick + replication time slices.
  • net.Debug.ActorClassNameTypeCSV 1 (5.6+, dedicated servers, Non-Shipping) — per-class CSV stats for ForceNetUpdate / FlushNetDormancy calls/sec. The new "where is my dormancy regression" stat.
  • Insights with -tracehost from the dedicated server — full timing in real-time.
  • obj list class=Actor outer=World — live actor count on the server (sanity check vs replicated count).
✓

The 30 vs 60 Hz tradeoff matrix

The right tick rate depends on game design and content scale, not just preference:

GenreRecommended HzReplication strategyReference
Battle Royale (100 players)30Replication Graph + dormancy + adaptive freqFortnite
Competitive FPS (10–32 players)60Iris (5.6+) or RepGraph + push modelLyra reference
Co-op shooter (4–16 players)60Default NetDriver + push model + dormancyMany shipped titles
MMO (100+ players, large world)20–30Replication Graph + heavy dormancy + Iris when stableWP-streamed servers
Fighting / 1v160Default + fixed-tick networked physicsLyra-class

PerfGuard baselines per scenario can capture an Insights trace from the headless server under a scripted load (N AI bots, M replicated actors), and gate two metrics in CI: server frame time at p99, and NetBroadcastTickTime as a percentage of frame budget. Bonus: scrape the 5.6 net.Debug.ActorClassNameTypeCSV output to flag any class whose ForceNetUpdate / FlushNetDormancy calls/sec doubled vs baseline — the dormancy-regression early-warning signal.