A single dedicated machine with 16 fast cores, 128 GB of RAM, and two NVMe Gen4 drives can host eight to twelve game server instances at once, and the per-slot cost drops by a factor of three to five compared with renting an individual server per game. The catch is operational: without disciplined process management, one leaking instance takes down the whole box. SteamCMD plus systemd template units solves that with per-instance users, cgroup memory limits, graceful shutdown signals, and scheduled updates.
Sizing the machine around the instances
Start from the instance list, not the CPU. Each game has a different RAM floor and a different relationship with clock speed:
| Game | Steam App ID | RAM / instance | Net up (32 players) | Tick sensitivity |
|---|---|---|---|---|
| Valheim | 896660 | 3–4 GB | 1.5 Mbps | High (single-thread) |
| Palworld | 2394010 | 12–16 GB | 3 Mbps | High |
| Project Zomboid | 380870 | 2–3 GB | 1 Mbps | Medium |
| 7 Days to Die | 294420 | 6–8 GB | 2 Mbps | High |
| ARK: Survival Ascended | 2430930 | 16–24 GB | 4 Mbps | Very high |
| Rust | 258550 | 8–16 GB | 5 Mbps | Very high |
| Counter-Strike 2 | 730 | 4 GB | 8 Mbps (128-tick) | Very high |
| Terraria (tModLoader) | 105600 | 1–2 GB | 0.5 Mbps | Low |
A realistic 128 GB build: four ARK: Survival Ascended maps at 22 GB each (88 GB), two Valheim instances at 4 GB, one Project Zomboid at 3 GB, and 8 GB left for the OS, systemd, and the SteamCMD update spool. That is seven instances on a Ryzen 9 7950X3D with two 2 TB NVMe Gen4 drives — one for worlds and saves, one for the OS and Steam depot cache.
Directory and user layout
# One system user per instance keeps ownership and ports isolated
sudo useradd -r -m -d /srv/gameservers/valheim1 -s /usr/sbin/nologin valheim1
sudo useradd -r -m -d /srv/gameservers/asa1 -s /usr/sbin/nologin asa1
# Layout
/srv/gameservers/<instance>/ # install dir (SteamCMD force_install_dir)
/srv/gameservers/<instance>/data/ # worlds, saves, configs
/etc/gameservers/<instance>.env # per-instance variables
One systemd template unit for every instance
A single template file covers all instances. The %i placeholder expands to the instance name, which is also the Linux user and the environment file basename.
# /etc/systemd/system/[email protected]
[Unit]
Description=Game server instance %i
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=%i
Group=%i
EnvironmentFile=/etc/gameservers/%i.env
WorkingDirectory=/srv/gameservers/%i
ExecStartPre=/usr/games/steamcmd +force_install_dir /srv/gameservers/%i \
+login anonymous +app_update ${STEAM_APPID} validate +quit
ExecStart=/srv/gameservers/%i/${START_CMD}
Restart=on-failure
RestartSec=15
KillSignal=SIGINT
TimeoutStopSec=180
LimitNOFILE=100000
MemoryMax=${MEM_MAX}
CPUQuota=${CPU_QUOTA}
CPUWeight=200
Nice=-5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
# /etc/gameservers/asa1.env
STEAM_APPID=2430930
START_CMD=ShooterGame/Binaries/Linux/ArkAscendedServer TheIsland_WP?listen?Port=7777?QueryPort=27015?MaxPlayers=50 -server -log
MEM_MAX=24G
CPU_QUOTA=600%
# /etc/gameservers/valheim1.env
STEAM_APPID=896660
START_CMD=valheim_server.x86_64 -name "Community" -port 2456 -world Dedicated -public 1 -savedir /srv/gameservers/valheim1/data
MEM_MAX=4G
CPU_QUOTA=200%
Two settings do the heavy lifting. KillSignal=SIGINT tells most game servers to flush their world to disk before exiting — without it, a restart can roll back to the last autosave. And MemoryMax means a Palworld memory leak hits a cgroup wall and restarts one instance instead of triggering the kernel OOM killer and taking down every server on the machine.
Starting, stopping, and watching instances
sudo systemctl daemon-reload
sudo systemctl enable --now gameserver@valheim1 gameserver@pz1 gameserver@asa1
sudo systemctl status 'gameserver@*' --no-pager
sudo journalctl -u gameserver@asa1 -f
sudo systemctl restart gameserver@asa1 # SIGINT, then restart
sudo systemctl stop gameserver@asa1 # graceful save on exit
systemd-cgtop # per-instance CPU and memory
Scheduled updates without killing players
Run SteamCMD updates on a timer rather than on boot only, and use a per-instance lock so two instances never validate the same depot simultaneously. Large updates can rewrite 20–40 GB, so schedule them off-peak and broadcast a countdown over RCON first.
# /etc/systemd/system/[email protected]
[Unit]
Description=SteamCMD update for %i
[Service]
Type=oneshot
User=%i
ExecStart=/usr/bin/flock /run/gameserver-%i.lock /usr/games/steamcmd \
+force_install_dir /srv/gameservers/%i +login anonymous \
+app_update ${STEAM_APPID} validate +quit
# /etc/systemd/system/[email protected]
[Unit]
Description=Daily update check for %i
[Timer]
OnCalendar=*-*-* 05:00
Persistent=true
RandomizedDelaySec=30m
[Install]
WantedBy=timers.target
Then systemctl enable --now [email protected] for each instance. RandomizedDelaySec staggers the jobs so eight SteamCMD processes do not saturate the uplink at 05:00.
Port planning
Every instance needs a unique game port and query port. Reserve a block of 16 per instance and document it, because port collisions are the most common reason a second instance fails to appear in the server browser:
| Instance | Game port | Query port | RCON port |
|---|---|---|---|
| valheim1 | 2456 | 2457 | 2458 |
| asa1 | 7777 | 27015 | 27020 |
| pz1 | 16261 | 16262 | 16263 |
| cs2-1 | 27015 | 27016 | 27017 |
| rust1 | 28015 | 28016 | 28017 |
Disk I/O isolation and backups
Multiple instances share one storage device, which makes I/O isolation the second-biggest failure mode after RAM. Two habits prevent it:
- Separate world saves from the Steam depot. Updates rewrite tens of gigabytes; if that write runs on the same NVMe as eight world directories, autosaves will stall behind it. Use one Gen4 drive for
/srv/gameservers/*/dataand a second for/usr/games/steamcmdand depot caches. - Throttle backups. An rsync or restic job that reads eight worlds at full disk speed will add hundreds of milliseconds of latency to every instance; run it with
ionice -c2 -n7andnice -n 19, or use cgroupIOWeightper instance. - Snapshot per instance. A nightly
systemctl stop gameserver@xis too disruptive; instead rely on filesystem snapshots (ZFS or btrfs) so one corrupt world is recoverable in seconds without touching the other seven. - Watch inode and disk-space alarms. Five instances writing logs and world backups will fill a 2 TB drive faster than expected; alert at 80%.
Storage layout on a two-drive box: NVMe A holds the OS, SteamCMD, and depot cache; NVMe B holds every instance’s install directory and world data. If a game server must write saves and read its own binary from the same device, keep at least 25% of the drive free so garbage collection on the SSD does not stall writes.
Operational habits that keep the box up
- Alert on cgroup memory pressure per instance with
systemd-cgtopor node_exporter, not just overall RAM. - Snapshot the worlds directory nightly; a corrupt save on one instance should never require a full-machine restore.
- Keep the Steam depot cache on a separate NVMe from the world saves so an update download cannot stall an autosave.
- Reboot the whole machine monthly, with instances stopped gracefully first, to clear firmware-level issues.
Running many instances on one box works best when the hardware underneath is genuinely dedicated and stable — an oversubscribed host makes per-instance cgroup tuning pointless. If you are still shortlisting machines for this setup, review dedicated server specs for game hosting and prioritise high single-thread clocks, NVMe Gen4, and 128 GB of ECC RAM. Our dedicated server comparison covers which providers offer those configurations with the memory headroom multi-instance hosting demands.


Leave a Reply
You must be logged in to post a comment.