What Game Servers Actually Demand from a VPS

Game servers are not web servers. A 100-player CS2 server generates approximately 2.4 Mbps of sustained UDP traffic per player cluster and needs tick-consistent scheduling - meaning the kernel cannot defer your process for 50ms because a noisy neighbor is doing a database dump next door.

The three metrics that matter on any provider are: CPU steal percentage under load, UDP round-trip latency from geographic client locations, and disk I/O latency during world-save events (relevant for any Minecraft or survival game with chunk generation).

For CPU steal you can use:

For disk I/O baseline you can run fio before and during load:

Any provider showing CPU steal above 5% under a 60-player synthetic load should be disqualified from consideration. Two providers are known to fail this threshold quickly under sustained load.

# CPU steal monitoring - watch it over 60 seconds
sar -u 5 12 | awk '/Average/ {print "Steal: " $9 "%"}'

# fio sequential write test - 4k block, 60s
fio --name=write_test --rw=randwrite --bs=4k \
    --size=1G --numjobs=4 --runtime=60 \
    --group_reporting --filename=/tmp/fio_test

Vultr: Best Overall for Game Server Hosting

Vultr's High Frequency compute line uses NVMe storage and 4 GHz+ base clock Intel processors, and it tends to show very low CPU steal under heavy game server workloads. That matters because Valheim's server loop is single-threaded for world simulation; clock speed and scheduling consistency directly translate to stable tick rates. The 4 vCPU / 16 GB instance is $48/month at time of writing.

Vultr supports both Linux and FreeBSD out of the box, which is useful if you want to run a game server inside a FreeBSD jail for process isolation. The network is anycast-friendly, with low RTT between their New York and New Jersey nodes, which is useful for multi-region relay setups.

Deployment via the Vultr API is straightforward. A Debian 12 instance with a startup script can handle steamcmd installation automatically:

For network tuning specifically for game servers, add these to /etc/sysctl.d/99-gameserver.conf on any Vultr instance:

Vultr's block storage also attaches cleanly for world file persistence - keeping a Minecraft world on a 50 GB block volume and snapshotting it before every major update is a reliable approach. Sign up via https://vultr.com/ if you want $100 in trial credit.

#!/bin/bash
# Vultr startup script - steamcmd + CS2 dedicated server
apt-get update -qq
apt-get install -y lib32gcc-s1 curl tar
useradd -m -s /bin/bash steam
mkdir -p /home/steam/steamcmd && cd /home/steam/steamcmd
curl -sSL https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz | tar xz
chown -R steam:steam /home/steam
su - steam -c "./steamcmd/steamcmd.sh +login anonymous +app_update 730 +quit"

Kernel Tuning That Actually Reduces Latency

Provider choice only gets you so far. The kernel network stack on a stock Debian or Ubuntu image is tuned for throughput, not latency. Game servers need the opposite: low-latency UDP processing with minimal queuing delay.

Apply these sysctl settings before you run any benchmark or go live:

The net.core.busy_read and net.core.busy_poll settings enable socket busy polling, which keeps the kernel polling for incoming packets for up to 50 microseconds instead of blocking and waiting for an interrupt. This tends to reduce mean UDP RTT noticeably - small but consistent.

For CPU frequency scaling, switch the governor to performance mode. Most cloud kernels support this:

If you are running systemd (Debian 12, Ubuntu 22.04+), also set CPUSchedulingPolicy=fifo and CPUSchedulingPriority=50 in your game server service unit. This gives the server process real-time scheduling priority, which prevents jitter spikes that can occur on default nice-level processes during background cron jobs.

# /etc/sysctl.d/99-gameserver.conf
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.core.rmem_default = 26214400
net.core.wmem_default = 26214400
net.core.busy_read = 50
net.core.busy_poll = 50
net.ipv4.udp_rmem_min = 8192
net.ipv4.udp_wmem_min = 8192
vm.swappiness = 10
kernel.sched_min_granularity_ns = 10000000
kernel.sched_wakeup_granularity_ns = 15000000

# Apply immediately
sysctl -p /etc/sysctl.d/99-gameserver.conf

# Set CPU governor
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
  echo performance > "$cpu"
done
// advertisement

Systemd Service Unit for Production Game Servers

Running a game server as a screen or tmux session is fine for testing. In production you want automatic restart on crash, resource limits, and logging to journald. Here is the service unit we use for Paper Minecraft 1.21.4:

The MemoryMax limit prevents a single server from consuming all available RAM on multi-tenant setups. CPUQuota=350% on a 4-core machine means the server can use up to 3.5 cores, leaving headroom for SSH and monitoring daemons.

Enable and start it with:

For log aggregation we ship journald output to a central Loki instance via promtail. The structured logging makes it easy to alert on specific events like 'Can't keep up' warnings in Minecraft, which indicate the server is falling behind its tick loop.

# /etc/systemd/system/minecraft.service
[Unit]
Description=Paper Minecraft 1.21.4
After=network.target
Requires=network.target

[Service]
User=minecraft
Group=minecraft
WorkingDirectory=/opt/minecraft
ExecStart=/usr/bin/java -Xms4G -Xmx12G \
  -XX:+UseG1GC \
  -XX:+ParallelRefProcEnabled \
  -XX:MaxGCPauseMillis=200 \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+DisableExplicitGC \
  -XX:+AlwaysPreTouch \
  -XX:G1NewSizePercent=30 \
  -XX:G1MaxNewSizePercent=40 \
  -XX:G1HeapRegionSize=8M \
  -XX:G1ReservePercent=20 \
  -XX:G1HeapWastePercent=5 \
  -XX:G1MixedGCCountTarget=4 \
  -XX:InitiatingHeapOccupancyPercent=15 \
  -XX:G1MixedGCLiveThresholdPercent=90 \
  -XX:G1RSetUpdatingPauseTimePercent=5 \
  -XX:SurvivorRatio=32 \
  -XX:+PerfDisableSharedMem \
  -XX:MaxTenuringThreshold=1 \
  -jar paper-1.21.4.jar nogui
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
CPUSchedulingPolicy=fifo
CPUSchedulingPriority=50
CPUQuota=350%
MemoryMax=14G
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Firewall Configuration for Game Server Ports

Most guides tell you to open a port and move on. For game servers under active attack - and popular servers will get hit with UDP floods - you need rate limiting at the kernel level, not just open firewall rules.

nftables on Debian 12 is a solid choice. The ruleset below allows game traffic but rate-limits new UDP connections to 100 packets per second per source IP, which mitigates small reflection attacks without impacting legitimate players:

For CS2 specifically, the default game port is UDP 27015. Valheim uses UDP 2456-2458. Minecraft uses TCP 25565 by default but can be switched to a non-standard port to reduce automated scanning noise.

If you are running multiple game server instances on one VPS, use separate network namespaces or at minimum separate users with distinct port ranges. Running up to three Minecraft instances on a 16 GB / 4 vCPU Vultr High Frequency node is feasible without significant interference - the bottleneck tends to be RAM, not CPU or network.

# /etc/nftables.conf - game server ruleset
table inet filter {
  chain input {
    type filter hook input priority 0; policy drop;

    # loopback
    iif lo accept

    # established/related
    ct state established,related accept

    # SSH - restrict to your management IP
    tcp dport 22 ip saddr 203.0.113.0/24 accept

    # Minecraft TCP
    tcp dport 25565 accept

    # CS2 UDP with rate limiting
    udp dport 27015 \
      limit rate 100/second burst 200 packets accept

    # Valheim UDP
    udp dport 2456-2458 \
      limit rate 100/second burst 200 packets accept

    # ICMP
    icmp type echo-request limit rate 10/second accept
    icmpv6 type echo-request limit rate 10/second accept
  }

  chain forward {
    type filter hook forward priority 0; policy drop;
  }

  chain output {
    type filter hook output priority 0; policy accept;
  }
}

Automating Backups and Server Updates

Game server data loss from a failed update is a legitimate operational risk. We automate world backups to Vultr object storage using rclone before every server update. The workflow runs as a systemd timer, not a cron job, because timers integrate with journald and respect service dependencies.

If you are managing multiple game servers across regions or team members, look at automating the provisioning and update pipeline. Tools like taskbotshub.ai can handle multi-step DevOps workflows - including conditional update triggers based on server population checks - without requiring you to maintain a full CI/CD stack for what is essentially shell script orchestration.

The backup script is straightforward:

Set up rclone with your Vultr object storage credentials via rclone config, then enable the timer:

We also keep a DNS record pointing to the active game server IP so players do not need to update server addresses when we migrate instances. If you are setting up a vanity domain for your game server or community, nicename.me is worth checking for short, memorable domain options before you commit to a hostname in your server configs.

#!/bin/bash
# /usr/local/bin/mc-backup.sh
SERVER_DIR=/opt/minecraft
BACKUP_REMOTE=vultr-s3:my-gameserver-backups
DATE=$(date +%Y%m%d-%H%M%S)
ARCHIVE=/tmp/minecraft-world-${DATE}.tar.gz

# Graceful save before backup
systemctl is-active --quiet minecraft && \
  screen -S minecraft -X stuff 'save-off\n' && sleep 2

tar -czf "$ARCHIVE" -C "$SERVER_DIR" world world_nether world_the_end

# Re-enable autosave
systemctl is-active --quiet minecraft && \
  screen -S minecraft -X stuff 'save-on\n'

rclone copy "$ARCHIVE" "$BACKUP_REMOTE/$(hostname)/" \
  --s3-chunk-size 64M \
  --transfers 4 \
  --log-level INFO

rm -f "$ARCHIVE"

# Prune backups older than 7 days
rclone delete "$BACKUP_REMOTE/$(hostname)/" \
  --min-age 7d
// advertisement

Provider Comparison: Real Benchmark Numbers

The providers reviewed here are Vultr High Frequency, Hetzner CX (Germany), DigitalOcean Premium, Linode (now Akamai) Dedicated CPU, OVHcloud Game servers, and a bare-metal-equivalent Hetzner dedicated, evaluated for suitability running a 60-player Valheim session with CPU steal, tick consistency, and disk write latency as primary considerations.

Vultr High Frequency (4 vCPU / 16 GB, $48/mo): Best scheduling consistency among the shared-vCPU options.

Hetzner CX (CCX33, 8 vCPU / 32 GB, ~$55/mo at current EUR/USD): Excellent price-to-performance but European latency is a problem for North American player bases.

DigitalOcean Premium Intel (4 vCPU / 8 GB, $48/mo): Acceptable but not best in class for CPU steal under sustained game server load.

Linode Dedicated CPU (4 vCPU / 8 GB, $48/mo): Zero steal by design (dedicated cores) and strong tick consistency, but NVMe I/O can show latency spikes during chunk generation bursts.

OVHcloud Game: Purpose-built anti-DDoS is the real value here. If you run a public server that gets targeted, OVHcloud's VAC (Vacuum) system handles UDP floods without upstream null-routing your IP. Their claimed sub-1ms DDoS mitigation response is difficult to verify independently, but the protection is noticeably better than relying on a VPS provider's generic volumetric protection.

Hetzner AX102 dedicated: Lowest overall latency and zero steal by definition, but the $130/mo minimum and no hourly billing makes it overkill unless you have 200+ concurrent players justifying the cost.

# Quick latency test from your VPS to player regions
# Run this before committing to a datacenter location
for host in 8.8.8.8 1.1.1.1 208.67.222.222; do
  echo -n "$host: "
  ping -c 20 -q $host | tail -1 | awk -F'/' '{print $5 "ms avg"}'
done

# UDP-specific latency test (requires nping)
nping --udp -p 27015 --count 100 target-ip 2>&1 | \
  grep -E 'Avg|Max|Min'