Kembali ke Blog

DDoS Protection for Game Servers: A Runbook for the 10-Minute Attack Era

Diterbitkan pada 30 Oktober 2026
DDoS Protection for Game Servers: A Runbook for the 10-Minute Attack Era Dibuat dengan bantuan AI

Ringkasnya

Write a DDoS protection for game servers runbook: detect sub-10-minute bursts, triage UDP floods, and harden your game backend before players notice.

Your matchmaking queue stalls at 21:14. By 21:19 it is back. You check the graph and find a 620 Mbps spike, six minutes wide, already gone before the alert reached your phone. Nobody on your two-person team saw it live, and your players filed it under "lag."

That shape of attack is now the default. Cloudflare's Q3 2026 DDoS Threat Report counted 18.2 million network-layer attacks mitigated in the quarter — roughly 8,226 per hour, or one every 0.44 seconds. Of those, 96% ended in under 10 minutes and 98% stayed under 1 Gbps. Hyper-volumetric attacks above 1 Tbps fell from 805 in Q2 to 251 in Q3, a ~69% drop the report attributes to law enforcement takedowns of the Aisuru, KimWolf, JackSkid and Mossad IoT botnets.

Fewer giants. More swarms. And the swarm is the threat model a small game backend is worst prepared for, because it is simultaneously too small to trigger your provider's mitigation tier and large enough to saturate a single game instance.

This is a runbook for that bandwidth band.

What Q3 2026 actually changed

Three shifts matter if you operate game servers. The first is duration. A 208% year-over-year surge in short-burst, low-bandwidth attacks is the operational signature of DDoS-for-hire "booter" services: cheap, repeatable, and fired in volleys. Expect 40 attacks across an evening, not one.

The second is vector churn. CLDAP reflection — abused public domain controllers on UDP 389 — collapsed 99.5% quarter-over-quarter to just 0.05% of attacks, after running hot through H1. Attackers did not stop; they rotated. DNS amplification jumped 215% to 3.43% of network-layer attacks, and general UDP floods climbed from 14.06% to 31.31%.

The third is that targets broadened. Media stayed the most-attacked industry but lost about three quarters of its Q2 share. Internet infrastructure rose from outside the top 10 to #6, and Consumer Services and Adult Entertainment entered at #8 and #9. Games sit squarely in that cluster of consumer-facing, latency-sensitive targets.

The lesson for defenders: do not build your playbook around last quarter's vector. Build it around the four categories — UDP flood, DNS amplification, reflection abuse, and SYN exhaustion — and assume the weights keep moving.

What breaks first in a game backend

Not everything fails at once, and knowing the order saves you from fighting the wrong fire.

  • The game port. A UDP flood on your authoritative server port consumes NIC queues before CPU. Your server appears "up" to your orchestrator health check because the process is alive, while packets are being dropped in the kernel.
  • Session and login endpoints. HTTP floods hit auth. Players who disconnect mid-burst cannot get back in, and the ones already in-game churn out because the reconnect loop fails.
  • Leaderboard reads. These are the single most amplified read path in most game backends: one client request fans out into a database range scan. 5,000 requests per second is trivial to send and expensive to serve.
  • Telemetry ingestion. Often unauthenticated or rate-limited loosely, often the cheapest endpoint to hammer.
  • The conntrack table. Filling it takes down every service on the host, including your SSH access, which is how a four-minute attack turns into a four-hour outage.

Reflection attacks deserve a specific note: the traffic arrives from legitimate DNS resolvers and other third-party servers, so IP reputation blocking fails by design. You are being hit by innocent infrastructure.

Detection: the signals that fire before the graphs

Your dashboards lag. Kernel counters do not. The pre-indicator that matters most is the packet-per-second to bit-per-second ratio. A legitimate 300 Mbps of game traffic is roughly 25,000-40,000 packets per second at typical payload sizes. A 300 Mbps UDP flood with 64-byte packets is closer to 500,000 pps. Same bandwidth, 15x the packets, and it will kill the host while your bandwidth graph looks moderate.

Run these on the box under suspicion:

# socket and memory pressure summary
ss -s

# per-protocol counters and the ones that predict a lockup
nstat -az | grep -E 'UdpInDatagrams|UdpNoPorts|UdpInErrors|TcpExtListenDrops|TcpExtTCPReqQFullDrop'

# current conntrack table occupancy vs max
conntrack -C && sysctl net.netfilter.nf_conntrack_max

# what is actually arriving on the wire
tcpdump -nn -i eth0 'udp and not port 7777' -c 200

Read them like this:

  • UdpInDatagrams climbing while your game loop reports lower player counts = flood.
  • UdpNoPorts spiking = scanner sweeping your port range, often a precursor.
  • TcpExtTCPReqQFullDrop non-zero = SYN queue overflow, meaning you are already losing legitimate handshakes.
  • conntrack -C above 70% of nf_conntrack_max = you are minutes from total host failure, not just game server failure.

If you want the per-endpoint view rather than the kernel view, the UEFN server crash diagnostic protocol walks through distinguishing a network-layer stall from an engine-layer one, which is the first decision in any triage.

For in-process defense against the junk that does get through, a per-source token bucket at the socket layer is cheap and effective. This C# example gates inbound packets before they reach your simulation tick:

using System.Diagnostics;
using System.Net;

public sealed class PacketGate
{
    private readonly Dictionary<IPAddress, Bucket> _buckets = new();
    private readonly int _ratePerSecond;
    private readonly int _burst;

    // A legit client at 30 Hz sends ~30 packets/s. 120/s allows
    // headroom for retransmits without letting a flood through.
    public PacketGate(int ratePerSecond = 120, int burst = 240)
    {
        _ratePerSecond = ratePerSecond;
        _burst = burst;
    }

    public bool Allow(IPAddress source)
    {
        long now = Stopwatch.GetTimestamp();

        if (!_buckets.TryGetValue(source, out var bucket))
        {
            bucket = new Bucket { Tokens = _burst, LastRefill = now };
            _buckets[source] = bucket;
        }

        double elapsed = (now - bucket.LastRefill) / (double)Stopwatch.Frequency;
        bucket.Tokens = Math.Min(_burst, bucket.Tokens + elapsed * _ratePerSecond);
        bucket.LastRefill = now;

        if (bucket.Tokens < 1.0) return false;
        bucket.Tokens -= 1.0;
        return true;
    }

    private sealed class Bucket
    {
        public double Tokens;
        public long LastRefill;
    }
}

Be clear about what this does and does not do. It filters per-source junk that fits inside your available bandwidth. It does nothing once the pipe is full — at that point the packets are dropped upstream of your process regardless of what your code decides.

Remediation runbook: the first ten minutes

Since 96% of attacks end before a human can respond, the runbook's job is not to have you fix the attack. It is to have you capture evidence and reduce blast radius while the attack burns out.

T+0 to T+2 minutes — confirm, do not scale. Resist the reflex to add instances. Scaling into a flood multiplies your bill and your attack surface. Confirm the vector with the counters above, note the pps and bandwidth, and check whether the conntrack table is filling. If it is, drop to T+0 emergency host protection immediately.

T+2 to T+5 minutes — shed non-essential load. Disable everything that is not gameplay: telemetry batching, leaderboard refresh, storefront polling, social feeds. If your backend supports remote configuration, this is a single flag flip rather than a deploy.

T+5 to T+10 minutes — apply host-level rate limits. Kernel filtering is orders of magnitude cheaper than application filtering:

table inet gameguard {
    set udp_flood {
        type ipv4_addr
        flags dynamic, timeout
        timeout 60s
    }

    chain input {
        type filter hook input priority filter; policy drop;

        iif lo accept
        ct state established,related accept
        tcp dport { 22, 443 } accept
        tcp dport 7777-7800 accept
        udp dport 7777-7800 accept

        # drop fragmented UDP - your game protocol should never need it
        ip frag-off & 0x1fff != 0 udp drop

        # per-source UDP cap well above a real client's send rate
        udp dport 7777-7800 update @udp_flood \
            { ip saddr limit rate over 200/second burst 400 packets } drop
    }
}

Load with nft -f gameguard.nft. Verify with nft list ruleset and nft list set inet gameguard udp_flood. If you get this wrong and lock yourself out, you have converted a network incident into a physical-access incident — test it on staging first, and keep the rules in version control.

T+10 minutes onward — escalate to your upstream. You need scrubbing or blackhole capability at the network edge, not on the host. Most managed game hosting providers offer this; if yours does not, that is a procurement problem to solve before the next attack. Ask specifically for their sub-1 Gbps UDP mitigation threshold, because most published SLAs are written for terabit attacks you will never see.

Throughout: keep a timestamped log. Attack start, vector, peak pps, peak Gbps, what you changed and when. The postmortem is worth more than the mitigation.

Vector playbook: matching the response to the shape

UDP flood (31.31% of attacks). Random or targeted ports, forcing the host to check for a listener and generate ICMP unreachable. The network layer fallback architecture article covers how to keep a degraded-but-playable mode available when the primary transport is being hammered. Response: nftables per-source rate limit, close all UDP ports that are not your game port, and disable ICMP unreachable generation at the host.

DNS amplification (3.43%, up 215%). Forged-source queries to open resolvers, large responses aimed at you. Response: you cannot block by source, because the sources are legitimate resolvers. You can only absorb or filter by response size and rate. Confirm you are not yourself running an open resolver with dig +short txt chaos version.bind @your.ip — if it answers, close it today.

CLDAP (collapsed to 0.05%). Currently quiet, historically enormous. The reminder is that CLDAP amplification runs on UDP 389, so unless you need LDAP, keep it filtered at both host and cloud firewall. Attackers return to unprotected ports when they rotate back.

SYN flood. Rarely volume, always exhaustion. Enable SYN cookies (sysctl -w net.ipv4.tcp_syncookies=1), raise net.core.somaxconn, and make sure the conntrack table is sized for your worst realistic case rather than the default.

Prevention: architecture that survives a four-minute burst

Capacity math first, because it kills a lot of wishful thinking. A 64-player, 100-tick server moves roughly 15-20 KB/s per player — call it 1.2 MB/s, or about 10 Mbps per instance. A 400 Mbps attack is 40x that instance's legitimate traffic. Since 98% of attacks sit under 1 Gbps, almost every attack in the current distribution is large enough to flatten a single game instance while being far too small to interest a tier-1 scrubbing provider's auto-mitigation.

That constrains your architecture in three ways.

Separate the account plane from the game plane. If your login endpoint, leaderboard reads, and player saves run on the same host as your game servers, a flood on the game port takes down accounts too, and players cannot even reach a "we'll be right back" screen. Running those account-bound services on horizOn — authentication, Cloud Save, and leaderboards are all managed there — means a burst against your game port degrades gameplay without locking every player out of their profile.

Make feature shedding a configuration change, not a deploy. When the flood hits, you have seconds. Remote configuration lets you turn off expensive read paths instantly. If your only option is pushing a build, you will not use it.

Build the health check to see network failure. A process check reports "healthy" while the kernel drops 90% of packets. Probe for actual gameplay reachability — a synthetic client that performs a real handshake and expects a response within 250ms — and let that drive your failover.

Best practices: five changes worth shipping this week

  1. Alert on packet rate, not just bandwidth. Set a threshold at 2.5x your 95th-percentile pps. Bandwidth-only alerting misses short bursts entirely; pps anomaly detection catches a 300 Mbps flood on a host with a 1 Gbps link that no bandwidth graph would flag.
  2. Size the conntrack table deliberately. Set net.netfilter.nf_conntrack_max based on peak concurrent sessions, not the distro default, and alert at 70% occupancy. This single change prevents the most common "attack became an outage" escalation.
  3. Pre-write the nftables ruleset and test it quarterly. Store it in your repo next to the deploy config. An untested firewall rule applied during an incident is a second incident.
  4. Publish a degraded-mode contract to your players. If leaderboards and chat go dark for ten minutes during mitigation, say so in the patch notes and the status page. Silence reads as a dead game. If you want a model for this, the resilience patterns in the post-compromise backend architecture breakdown translate directly to availability incidents.
  5. Log every attack as a structured event. Timestamp, vector, peak pps, peak Gbps, duration, mitigation applied. After five you will see your own pattern — most indie backends get hit on the same weekday, at the same hour, by the same booter.

The takeaway

The 2026 threat profile is hostile to teams that plan for terabit attacks and ignore the 400 Mbps six-minute burst. The giants are down 69%; the swarms are up 208%. Your mitigation has to be automated, cheap, and in the kernel, because no human is going to act inside an attack that ends before the alert is read.

Start with the counters, not the dashboards. Add the per-source rate limit, size the conntrack table, separate your account plane from your game plane, and rehearse the ten-minute runbook until it is boring.

If part of that separation is unglamorous work you would rather not own — player authentication, revision-aware Cloud Save, leaderboards, remote configuration you can flip mid-incident — that is the part horizOn handles, so your on-call time goes to the game servers instead. Try it free or read the docs before your next launch window.


Source: Cloudflare DDoS Threat Report 2026 Q3: More swarms, fewer giants