6.4 KiB
SteamCMD server notes
These notes describe the emerging pattern for SteamCMD-based game servers in this monorepo. They come from the Valheim prototype on Pikachu.
Goal
Use NixOS to manage server operations, not game content.
Nix owns service wiring, launch flags, ports, timers, users, and mounts. SteamCMD owns downloaded server files. The game owns world data, generated config, saves, and modded files.
Layering
Use three layers for SteamCMD-based servers.
configs/nixos/modules/services/steamcmd.nix
configs/nixos/modules/services/<game>.nix
configs/nixos/guests/<game>.nix
modules.services.steamcmd
This module provides the generic SteamCMD machinery.
It runs on any NixOS system. That system can be a physical host or a guest.
It owns:
- the
steamcmdservice user, - the shared SteamCMD data directory,
- per-instance Steam app updates,
- per-instance game service units,
- optional hard restart timers,
- firewall openings for declared ports.
It does not know Valheim, PalWorld, or Project Zomboid semantics. Game modules translate game-specific settings into SteamCMD instances.
modules.services.<game>
A game service module wraps modules.services.steamcmd.
For Valheim, this layer owns:
- Steam app id
896660, - the Valheim runtime app id,
- launch flags such as
-name,-world, and-port, - the expected UDP port range,
- the mapping from instance options to a SteamCMD instance.
This layer exposes operational config only. It avoids owning mutable gameplay files unless the game exposes stable launch flags.
guests.<game>
A guest module turns a game service into a deployable guest.
It owns host-facing concerns:
- VLAN placement,
- guest address registry entry,
- host paths and bind mounts,
- resource limits,
- secret file mounts,
- game-family instance declarations.
Do not add a reusable guests.steamcmd base guest.
The guest auto-loader turns configs/nixos/guests/steamcmd.nix into a concrete guest.
A specialized guest cannot use it as a base without creating another container.
Instance model
Use one guest per game family. Use named instances inside that guest.
For example, guests.palworld can contain instances.current and instances.old.
Each instance has its own ports and paths.
The host config must avoid clashes.
For Valheim, the prototype uses instances.foo.
The instance name is the operational identity.
The world name defaults to the instance name.
Keep these names separate in the module interface:
instance name -> unit names and paths
worldName -> game launch flag
This permits a test instance to load a copied world later.
Storage layout
Use three storage roots.
/data/steamcmd
/data/servers/<game>/<instance-name>
/media/games/servers/<game>/<instance-name>
/data/steamcmd
Store SteamCMD data and cache here.
This directory contains mutable Steam metadata and download state. Do not treat it as durable game state.
/data/servers/<game>/<instance-name>
Store the installed dedicated server files here.
SteamCMD writes these files. Mods can also mutate this tree. For that reason, use per-instance app installs. Do not share one app install between modded or historical instances.
/media/games/servers/<game>/<instance-name>
Store world data, saves, generated config, and durable game state here.
Backups are out of scope for the first prototype. This path gives a stable backup boundary later.
Updates
Do not pin game server versions in Nix. Steam controls the server build.
Nix controls when updates run. The prototype updates on service start. A daily hard restart timer can pull updates during a maintenance window.
The generic SteamCMD layer supports a restart timer. The default timer command restarts the instance service. Game modules or host config can override that command later.
Avoid generic player-warning or player-count logic in the SteamCMD layer. Those flows are game-specific.
Secrets
Use sops-backed environment files for durable configuration. Mount them read-only into the guest.
The Valheim prototype temporarily supports a hardcoded password. That password leaks into the Nix store and process arguments. Use it only for throwaway tests.
The target model is:
host sops secret -> read-only guest mount -> systemd EnvironmentFile
For Valheim, the environment file contains:
SERVER_PASSWORD=example-password
Networking
Open ports in the guest firewall from the game module.
Valheim uses the base UDP port through basePort + 2.
The default base port is 2456, so open UDP 2456-2458.
For public access, forward those UDP ports to the guest address. DNS is optional for the first deployment.
Use machinectl status <guest> to confirm the current DHCP address.
Do not trust a guessed address.
Add a DHCP reservation before depending on a committed address.
Valheim prototype
The prototype proved these facts:
- Valheim runs inside a NixOS guest on Pikachu.
- SteamCMD installs the dedicated server files at service start.
steam-runruns the mutable Valheim server binary.- LAN clients can connect to the guest address and UDP port.
- The storage split works for SteamCMD data, server files, and world data.
The prototype used:
guest: valheim
instance: foo
password: foobarbaz
actual DHCP address during test: 10.23.20.197
The hardcoded password and test instance name need cleanup before a final PR.
Bring-up checklist
- Add or update
modules.services.<game>. - Map game-specific options to
modules.services.steamcmd.instances. - Add or update
guests.<game>. - Mount
/data/steamcmdinto the guest. - Mount
/data/servers/<game>/<instance-name>into the guest. - Mount
/media/games/servers/<game>/<instance-name>into the guest. - Add a sops environment file for secrets.
- Open the required game ports in the guest firewall.
- Enable the guest on the target host.
- Deploy with
deploy-to <host>. - Check
container@<game>.serviceon the host. - Enter the guest with
nixos-container root-login <game>. - Check the game service with
systemctl status. - Follow logs with
journalctl -u <unit> -f. - Connect from LAN before adding public forwarding.
Open cleanup items
- Replace hardcoded prototype passwords with sops environment files.
- Choose durable instance names before long-term use.
- Add DHCP reservations for game guests.
- Add backup policy for world and modded server data.
- Decide whether server install trees need backup for modded servers.