Game Server Network Optimization: UDP, Kernel, and NIC Tuning to Cut Latency

Latency on a game server is decided mostly by physics and packet scheduling, not by the raw speed of your NIC. You can rent a 10 Gbps port and still give players 120 ms of rubber-banding if your UDP buffers are undersized, your kernel is running the wrong congestion control for a real-time workload, or your CPU is spending cycles on softirqs that should belong to the simulation thread. This guide walks through the network stack configuration that measurably reduces lag on a dedicated game host, with concrete sysctl values and the reasoning behind each.

Why The Default Kernel Stack Is Wrong For Games

Linux ships defaults tuned for throughput on file servers and web workloads. That means large receive buffers, CUBIC congestion control, and aggressive interrupt coalescing. Every one of those choices trades latency for bandwidth. A game server does the opposite trade: packets are tiny (60-1400 bytes), the frame rate is 20-128 Hz, and the value of a packet decays to zero within one tick interval. A block-break acknowledgment that arrives 80 ms late is worthless even if it arrived on a perfectly efficient 10 Gbps link.

The three levers that matter most are the UDP receive buffer, the NIC transmit queue length, and the softirq processing model. Fix those three and you will resolve the majority of “why does my server feel laggy when ping is fine” complaints. The rest — congestion control, MTU, checksum offload — are refinements that matter most at high player counts or on noisy uplinks.

Buffer Sizing: The Highest-Value Change

Under peak load, a game server can burst thousands of small UDP datagrams per second. If the socket receive buffer fills, the kernel silently drops packets — and for UDP there is no retransmit, so players simply miss state updates. Raise the ceiling first, then set the application buffer. Add these to /etc/sysctl.d/99-game.conf:

  • net.core.rmem_max = 16777216 — allows a 16 MB max UDP receive buffer.
  • net.core.wmem_max = 16777216 — matching send buffer ceiling.
  • net.core.rmem_default = 2097152 — 2 MB default for new sockets.
  • net.core.netdev_max_backlog = 16384 — deeper queue before the kernel drops during bursts.
  • net.core.somaxconn = 4096 — helps TCP game control channels (Steam, RCON).

Then set the buffer at the application level too. Most game servers accept a socket buffer config, but for anything you launch yourself, wrap it or set the socket option in the launcher. Verify effectiveness by watching netstat -su and checking the “receive buffer errors” counter during peak load — it should stay flat. If it climbs, your buffer is still too small or you have a CPU-side processing problem, not a network one.

Interrupt Coalescing Is Stealing Your Responsiveness

Modern NICs batch interrupts to save CPU. That is great for a webserver and terrible for a game server, because it adds 50-200 microseconds of jitter to every packet. Disable coalescing on your game interface:

ethtool -C eth0 rx-usecs 0 tx-usecs 0 adaptive-rx off adaptive-tx off. Then set the ring buffers to their maximum with ethtool -G eth0 rx 4096 tx 4096 (adjust to what ethtool -g eth0 reports as maximum). Also pin the NIC’s interrupt queues away from the cores running your game threads using irqbalance exclusions or manual /proc/irq/*/smp_affinity writes. On a 16-core box, dedicating cores 0-1 to network IRQ handling and leaving 2-15 for game instances keeps the tick loop clean.

Transmit Queue and Congestion Control

A too-short transmit queue causes drops under burst; a too-long one adds latency because packets sit in the queue. For game servers, 1000-2000 packets of queue length is the sweet spot. Set net.core.wmem_default and use tc qdisc replace dev eth0 root fq_codel. fq_codel gives you fair queuing plus active queue management, which prevents a single burst of state syncs from adding bufferbloat latency to your tick packets.

Set net.ipv4.tcp_congestion_control = bbr if you run TCP-based game control or asset delivery alongside the game — BBR handles lossy uplinks far better than CUBIC and keeps RTT low under load. For pure UDP game traffic, congestion control does not apply, which is another reason to get the buffer and IRQ settings right instead of relying on TCP tuning. If your provider hands you 1 Gbps by default, verify you are not saturating it before blaming the stack — a saturated port drops indiscriminately regardless of qdisc.

MTU, Checksums, and Offloads

Leave MTU at 1500 unless your whole path supports jumbo frames. A fragmented game packet is a lost packet, because losing one fragment loses the whole datagram. Verify path MTU through the uplink your provider uses with ping -M do -s 1472. Keep checksum offload enabled — ethtool -K eth0 rx on tx on — since software checksums burn CPU you want for the simulation thread.

Enable net.ipv4.udp_rmem_min and net.ipv4.udp_wmem_min to guarantee a floor for UDP socket memory even under pressure. And disable net.ipv4.tcp_slow_start_after_idle if you run persistent control channels. These are the settings that separate a server that “pings 15 ms but lags” from one that feels instant.

Testing That Your Changes Actually Helped

Do not tune blind. Baseline with mtr --report --udp to a known-good external host, record tick-time percentiles from your game server’s own metrics, and log the 99th-percentile jitter. Apply one change at a time and re-measure. A 20% reduction in tick-time p99 is a real win; a change that only improves average ping by 2 ms with no p99 effect is not worth the complexity. Our bare-metal plans give you full kernel and ethtool control, which a managed VPS will not.

Where To Go From Here

Once buffers, coalescing, and qdisc are set, the next frontier is kernel-bypass and DPDK-style userspace networking for extreme cases, but most servers never need it. Start with the sysctl file, pin your IRQs, set fq_codel, and watch your drop counters. If you are choosing hardware for this work, review our dedicated server comparison to confirm the NIC supports the offload and queue features you intend to use.

Leave a Reply