Game hosting

How to protect a Rust server against DDoS and massive lag?

May 28, 2026 · 10 min read
Illustration immersive du sujet : protect Rust server DDoS

To protect Rust server DDoS attacks in 2026, kernel-level packet filtering with XDP and nftables offers the fastest possible mitigation—processing and dropping malicious traffic before it reaches the network stack or application layer. This guide covers the complete technical architecture required to defend a Rust game server against volumetric floods, amplification attacks, and application-layer abuse using modern eBPF technology deployed directly on your Linux host.

Rust servers face unique DDoS challenges: their popularity attracts adversaries, UDP-based networking exposes them to amplification vectors, and even moderate packet floods (50,000–100,000 pps) can saturate a single CPU core running the game loop. Traditional userspace firewalls introduce latency and CPU overhead that degrades player experience; kernel-level filtering intercepts attacks at the earliest possible stage with sub-microsecond per-packet processing cost.



Why Kernel-Level Protection Is Critical for Rust Servers

Rust dedicates significant CPU resources to world simulation, player AI, and physics calculations. When a DDoS flood reaches the server's network stack, the kernel must allocate socket buffers, traverse the TCP/IP stack, and wake userspace processes—consuming cycles that should serve legitimate players. By the time packets reach a userspace firewall or the Rust binary itself, damage is already done: context switches, memory allocations, and interrupt handling have stolen resources from the game loop.

XDP (eXpress Data Path) operates at the network driver layer, before sk_buff allocation. An XDP program attached to your physical interface (typically eth0) inspects raw Ethernet frames and makes a verdict—XDP_DROP, XDP_PASS, or XDP_TX—within nanoseconds. This stateless, pre-stack filtering is ideal for volumetric DDoS: a single XDP program can process several million packets per second per core, dropping attack traffic before it consumes kernel memory or scheduler time.

Complementing XDP with nftables provides stateful connection tracking and TCP flag validation. XDP handles the high-volume, low-complexity filtering (IP blacklists, port-based blocks, packet-per-second rate limits), while nftables enforces connection state rules (dropping SYN floods that pass initial filters, rate-limiting new connections per source IP) and integrates with conntrack for legitimate session management.

Attack Vectors Targeting Rust Servers

  • UDP floods: Rust uses RakNet (UDP-based). Attackers send high-pps floods to game ports (28015–28016 default) to exhaust NIC queues and CPU interrupt handling.
  • Amplification attacks: Spoofed-source UDP queries to public resolvers, NTP servers, or memcached instances reflect magnified traffic to your server IP.
  • SYN floods: Half-open TCP connections to RCON ports (28016) or web query interfaces exhaust conntrack tables and backlog queues.
  • Application-layer abuse: Malformed RakNet packets or connection handshake spam that pass basic filters but trigger expensive parsing or state allocation in the Rust binary.

A dual-layer defense—XDP for stateless volumetric filtering, nftables for stateful session validation—addresses all four categories. PAKKT.io orchestrates both layers through a unified management interface, synchronizing rules between XDP BPF maps and nftables sets in real time.


Photorealistic datacenter aisle with rows of illuminated server racks in cyan and blue lighting, fiber optic cables organized in overhead cable trays, close-up of a network switch with SFP+ transceivers glowing green, representing high-performance network infrastructure for game server hosting


Deploying XDP for Stateless Packet Filtering

The PAKKT Engine is a single XDP program loaded onto your primary network interface, driven by up to 256 simultaneous rules stored in BPF maps. Each rule defines a port range, protocol (TCP/UDP/ICMP/any), action (block, rate_limit, allow_only), and optional rate-limit thresholds (global max_pps, per-port max_pps) plus packet size constraints (min/max bytes).

XDP Rule Configuration Example

To protect the primary Rust game port (28015 UDP) with a 100,000 pps rate limit and block all traffic to unused ports, you would configure:

# Rule 1: Rate-limit UDP 28015 to 100k pps globally
protocol: UDP
port_range: 28015-28015
rule_type: rate_limit
max_pps: 100000

# Rule 2: Allow UDP 28016 (RCON query) with 10k pps limit
protocol: UDP
port_range: 28016-28016
rule_type: rate_limit
max_pps: 10000

# Rule 3: Block all other UDP ports
protocol: UDP
port_range: 1-65535
rule_type: block
exceptions: 28015,28016

# Rule 4: Block ICMP (prevent ping floods)
protocol: ICMP
rule_type: block

These rules compile into eBPF bytecode and update the pakkt_rules BPF map. The XDP program performs a hash lookup per packet—O(1) complexity—then enforces the action. Rate-limit rules maintain per-rule packet counters in a separate BPF map, resetting every second via a userspace timer.

XDP operates before the kernel allocates socket buffers, so dropped packets consume only the minimal cycles required for driver RX processing and the XDP program itself. On a modern NIC with multi-queue support (Intel X710, Mellanox ConnectX), XDP can distribute load across CPU cores via RSS (Receive Side Scaling), achieving line-rate filtering even under multi-million-pps floods.

Packet Size Filtering

Many amplification attacks send small packets (64–128 bytes) or jumbo frames (>1500 bytes MTU) to bypass naive rate limits. PAKKT XDP rules support min_packet_size and max_packet_size constraints:

# Drop suspiciously small UDP packets (likely spoofed or amplification responses)
protocol: UDP
port_range: 28015-28015
rule_type: block
max_packet_size: 64

# Drop jumbo frames (fragmentation attacks)
protocol: any
rule_type: block
min_packet_size: 1501

This stateless size-based filtering complements rate-limits: even if an attacker stays under your pps threshold by sending fewer, larger packets, the size filter catches anomalies.


Close-up of a Linux terminal displaying eBPF program output with green monospaced text showing bpftool map dump commands, hexadecimal BPF map entries, and XDP statistics, dark background, realistic screen glow, representing kernel-level packet filtering diagnostics


Stateful Firewall Rules with nftables

While XDP excels at stateless filtering, connection-oriented attacks require stateful inspection. The PAKKT agent installs an isolated inet pakkt nftables table with dedicated chains for input, forward, and output traffic. This table coexists peacefully with Docker's nat table, fail2ban's filter table, and any existing iptables-persistent rules—preventing the configuration conflicts that plague traditional firewall stacks.

Connection Tracking and SYN Flood Mitigation

nftables integrates with the kernel's conntrack subsystem to track TCP connection states. A typical PAKKT nftables ruleset for a Rust RCON port (28016 TCP) looks like:

nft add rule inet pakkt input tcp dport 28016 ct state invalid drop
nft add rule inet pakkt input tcp dport 28016 ct state established,related accept
nft add rule inet pakkt input tcp dport 28016 ct state new limit rate 50/second accept
nft add rule inet pakkt input tcp dport 28016 drop

This ruleset:

  • Drops packets with invalid TCP flags or out-of-window sequence numbers (common in SYN floods).
  • Fast-paths established connections (legitimate RCON sessions) with zero additional checks.
  • Rate-limits new connections to 50 per second—blocking SYN floods while allowing legitimate clients to connect.
  • Drops any remaining packets (implicit deny).

The limit rate construct uses nftables' built-in token bucket: each new connection consumes one token, the bucket refills at 50 tokens/second. Once depleted, subsequent SYN packets are dropped until tokens regenerate. This is more CPU-efficient than per-IP accounting for high-cardinality attacks (botnets with thousands of source IPs).

Per-Connection Rate Limiting with Meters

For UDP-based protocols like Rust's game traffic, nftables meters provide stateful rate-limiting keyed by source IP:

nft add rule inet pakkt input udp dport 28015 meter ddos_udp { ip saddr limit rate over 1000/second burst 2000 packets } drop

This rule tracks packet rates per source IP. If any single IP exceeds 1,000 pps (with a 2,000-packet burst allowance for legitimate traffic spikes), subsequent packets from that IP are dropped. The meter automatically expires entries after inactivity, preventing memory exhaustion.

Combining XDP's global rate-limit (e.g., 100k pps total) with nftables' per-IP meter (1k pps per source) defends against both volumetric floods and distributed attacks. XDP handles the aggregate load; nftables isolates abusive sources.

Integration with Existing Firewall Rules

Because PAKKT uses a dedicated inet pakkt table, it never touches the filter, nat, or mangle tables used by Docker, Kubernetes, or legacy iptables tooling. You can query the PAKKT table in isolation:

nft list table inet pakkt

This architectural separation eliminates the "rule order conflicts" and "chain priority collisions" that occur when multiple firewall tools fight over the same netfilter hooks. Your existing SSH hardening, Docker port mappings, and fail2ban jails continue to function unchanged.



Real-Time Monitoring and Centralized Management

Manual eBPF map updates and nftables scripting are error-prone and unscalable. PAKKT.io provides a centralized web panel that synchronizes configuration, aggregates metrics, and visualizes attack traffic in real time. The lightweight Go agent (< 5 MB RAM, < 1% CPU) runs on each protected server, maintaining a persistent mTLS connection to the control plane with 30-second heartbeat intervals.

Dashboard Features

  • GeoIP visualization: World map and ranked lists of top source IPs and countries, updated every 30 seconds. Identify attack origins and apply geographic blocks if necessary.
  • Per-port and per-rule metrics: Time-series graphs (powered by TimescaleDB) showing packets passed, dropped, and rate-limited for each XDP rule and nftables chain. Correlate traffic spikes with player counts or attack events.
  • Audit log: Every configuration change (rule added, blacklist updated, rate-limit adjusted) is timestamped and attributed to the user or API call that triggered it.
  • Agent logs: Internal diagnostic output from the agent (XDP load errors, nftables syntax issues, mTLS reconnect attempts) surfaces in the panel for troubleshooting.

Dual-Layer IP Blacklist and Whitelist

PAKKT maintains synchronized blacklist and whitelist sets in both XDP (BPF map) and nftables (named set). When you add an IP to the blacklist via the panel or API, the agent:

  1. Updates the pakkt_blacklist BPF map, causing the XDP program to drop all packets from that IP immediately (sub-microsecond lookup).
  2. Appends the IP to the nftables @blacklist_v4 set, blocking any packets that bypass XDP (e.g., due to XDP offload limitations on certain NICs).

This dual-layer approach ensures defense-in-depth: even if XDP is unavailable (kernel version < 5.x, unsupported driver), nftables provides a fallback. Conversely, whitelisted IPs (your monitoring service, backup server, or static office IP) skip all rate-limits and blacklist checks in both layers.

Pterodactyl Integration

Game server hosts running Pterodactyl v1.x can install the PAKKT module to provision agents automatically when users deploy new servers. The integration:

  • Assigns one PAKKT agent per node (physical server), not per game server instance, because XDP operates at the NIC level.
  • Auto-configures port-based rules when Pterodactyl allocates ports to new servers.
  • Exposes per-server DDoS metrics in the Pterodactyl admin panel.

Pelican (Pterodactyl v2 fork) and WHMCS integrations are under development. For custom integrations, the PAKKT public API supports programmatic agent provisioning, rule management, and metrics export.



Practical Deployment Checklist

Deploying kernel-level DDoS protection requires careful planning to avoid service disruption. Follow this checklist when installing PAKKT on a production Rust server:

Pre-Deployment

  1. Verify kernel version: uname -r must report 5.x or higher. XDP requires CONFIG_BPF=y and CONFIG_XDP_SOCKETS=y in the kernel config. Most modern distributions (Ubuntu 20.04+, Debian 11+, CentOS 8+) ship compatible kernels.
  2. Check NIC driver support: Run ethtool -i eth0 and confirm the driver supports XDP native mode. Intel ixgbe/i40e, Mellanox mlx5, and Broadcom bnxt drivers have mature XDP support. Virtio (common in VPS environments) supports XDP generic mode (slower, but functional).
  3. Document existing firewall rules: Export current iptables/nftables state with iptables-save or nft list ruleset. PAKKT will not modify these, but baseline documentation aids troubleshooting.
  4. Whitelist critical IPs: Add your management IP, monitoring service, and any static upstream IPs to the PAKKT whitelist before enabling blocking rules. This prevents lockout.

Installation

  1. Sign up at PAKKT.io (7-day free trial on your first agent, then €3/agent/month).
  2. Generate an agent enrollment token in the web panel.
  3. SSH to your Rust server and run the one-line installer (provided in the panel). The agent binary is obfuscated with Garble and self-updates via SHA256-verified downloads.
  4. The agent auto-detects the primary NIC, loads the XDP program, and creates the inet pakkt nftables table.
  5. Configure initial rules in the web panel: start with permissive rate-limits (e.g., 500k pps global) and monitor traffic patterns for 24–48 hours before tightening thresholds.

Post-Deployment Validation

# Verify XDP program is loaded
ip link show dev eth0 | grep xdp

# Inspect BPF maps
bpftool map dump name pakkt_rules
bpftool map dump name pakkt_blacklist

# List nftables rules
nft list table inet pakkt

# Check agent status
systemctl status pakkt-agent
journalctl -u pakkt-agent -f

If the XDP program fails to load, check dmesg | grep xdp for verifier errors. Common issues include insufficient RLIMIT_MEMLOCK (raise with ulimit -l unlimited) or missing kernel BTF debug info (install linux-headers package).

Performance Tuning

  • NIC multi-queue: Use ethtool -L eth0 combined N (where N = number of CPU cores) to enable RSS. XDP programs run per-queue, so more queues = better parallelism.
  • CPU affinity: Pin the Rust server process to specific cores (taskset -c 0-3 ./RustDedicated) and leave other cores for XDP/softirq processing. Isolate IRQ handling with isolcpus kernel parameter for extreme workloads.
  • Conntrack tuning: If running many concurrent players, raise net.netfilter.nf_conntrack_max and net.netfilter.nf_conntrack_buckets in /etc/sysctl.conf.


Conclusion

Protecting a Rust server from DDoS in 2026 demands kernel-level packet filtering with XDP and nftables. XDP's stateless, pre-stack processing drops volumetric floods before they consume resources, while nftables' stateful connection tracking mitigates TCP-based attacks and per-source abuse. Deploying both layers in a unified, centrally managed platform eliminates manual eBPF scripting and rule conflicts. By filtering malicious traffic at the earliest possible point, you preserve CPU cycles for game logic and deliver consistent player experience even under sustained attacks. Kernel protection is no longer optional—it's the baseline for any public-facing game server in the modern threat landscape.



FAQ

Can XDP rate-limiting handle both small and large packet floods simultaneously?

Yes. XDP rules support both global max_pps thresholds (total packets per second across all sources) and optional min/max packet size filters. Configure one rule for aggregate rate-limiting and a second rule to drop packets outside normal size ranges (e.g., block < 64 bytes to mitigate amplification responses, or > 1500 bytes to stop fragmentation attacks). The XDP program evaluates all applicable rules in sequence, so multi-dimensional filtering is fully supported.

How does PAKKT's nftables table coexist with Docker port mappings?

PAKKT creates an isolated inet pakkt table with its own base chains at priority filter. Docker uses the nat table at priority -100 (pre-routing) for DNAT port mappings. Because netfilter evaluates tables in priority order and Docker's NAT rules run before PAKKT's filter rules, container port mappings are rewritten before PAKKT inspects the packet. PAKKT sees the post-DNAT destination (the container's internal IP), not the host's public IP, so you configure rules for the container port ranges as allocated by Docker. No manual coordination or chain jumping is required.

What happens if the PAKKT agent loses connection to the control plane during an attack?

The agent operates autonomously: XDP and nftables rules remain loaded and active in the kernel even if the agent process crashes or the mTLS connection drops. The 30-second heartbeat only synchronizes configuration changes and uploads metrics; it does not participate in the data plane. If connectivity is lost, the last-known-good ruleset continues filtering traffic. When the agent reconnects (automatic retry with exponential backoff), it reconciles any configuration drift and resumes metric uploads. For additional resilience, enable the agent's systemd watchdog to auto-restart on failure.

Protect your servers

Deploy PAKKT in 30 seconds

Dual-layer kernel protection. XDP + nftables. Driven from a central panel. 7-day free trial.