From 63284459765b9630ca090e566a3368faefd22b58 Mon Sep 17 00:00:00 2001 From: alexion Date: Thu, 10 Sep 2026 10:19:01 -0400 Subject: [PATCH] docs: update SteamCMD server workflow --- docs/00 INBOX/ai/steamcmd servers.md | 300 +++++++++++++++++++++++---- 1 file changed, 260 insertions(+), 40 deletions(-) diff --git a/docs/00 INBOX/ai/steamcmd servers.md b/docs/00 INBOX/ai/steamcmd servers.md index b8b46d5..62d68d9 100644 --- a/docs/00 INBOX/ai/steamcmd servers.md +++ b/docs/00 INBOX/ai/steamcmd servers.md @@ -1,28 +1,73 @@ # SteamCMD servers -Use `modules.services.steamcmd` as the low-level runner for one SteamCMD server. -Create a game module under `configs/nixos/modules/services/games/` for each game. +This note records the workflow used to add the Valheim server. +The workflow starts with the lowest useful layer. +Each later step moves game knowledge into a narrower module. -The game module owns game-specific defaults and command-line flags. -The host owns deployment-specific paths, names, secrets, and network placement. +## Stage 1: Run one server with `steamcmd` -## Create a game module +Start by proving the generic SteamCMD runner works on a host. +Configure `modules.services.steamcmd` directly in the host file. -1. Create `configs/nixos/modules/services/games/.nix`. -2. Define options under `modules.services.games.`. -3. Expose only values the host must choose. -4. Map those values to `modules.services.steamcmd`. -5. Set the Steam dedicated-server app ID in the game module. -6. Set the server executable in the game module. -7. Generate game-specific arguments in the game module. -8. Open the game's documented UDP ports in the game module. +```nix +modules.services.steamcmd = { + enable = true; + steamDir = "/pikachu/data/steamcmd"; + serviceName = "valheim"; + appId = 896660; + installDir = "/pikachu/data/servers/valheim/main"; + stateDir = "/pikachu/media/games/servers/valheim/main"; + executable = "./valheim_server.x86_64"; + arguments = [ + "-nographics" + "-batchmode" + "-name" + "main" + "-port" + "2456" + "-world" + "main" + "-savedir" + config.modules.services.steamcmd.stateDir + "-password" + "foobarbaz" + "-public" + "0" + ]; + udpPorts = [ + 2456 + 2457 + ]; +}; +``` -Keep `modules.services.steamcmd` focused on shared SteamCMD behavior. -Do not put game-specific flags or port rules there. +This layer owns SteamCMD update behavior and systemd wiring. +It also owns directory setup for SteamCMD metadata, server files, and state. -## Configure a host +Do not pin the game server version in Nix. +SteamCMD mutates the installed server files. -During validation, enable the game module directly on a host. +## Stage 2: Add a game service module + +After direct SteamCMD deployment works, create a game module. +Use `configs/nixos/modules/services/games/.nix`. + +The game module owns game-specific details: + +- Steam app ID +- server executable +- required command-line flags +- documented UDP ports +- private or public listing policy + +The host keeps deployment-specific values: + +- host paths +- server name +- secret choice +- guest placement, when a guest exists + +For Valheim, the host config became: ```nix modules.services.games.valheim = { @@ -35,37 +80,212 @@ modules.services.games.valheim = { }; ``` -Use host paths for direct host deployment. -Use guest paths after the server moves into a guest. +The Valheim module translated that into `modules.services.steamcmd`. +It generated the Valheim argument list and UDP ports. -## Deploy and test +## Stage 3: Add UDP ingress -1. Evaluate the host configuration. +Valheim uses UDP. +HTTP virtual hosts do not route Valheim traffic. - ```sh - nix eval .#nixosConfigurations..config.system.build.toplevel.drvPath --raw - ``` +Extend `registry.route` with UDP ports: -2. Deploy the host. +```nix +registry.route.pikachu.valheim = { + domain = "valheim.alexion.dev"; + udp.ports = [ + 2456 + 2457 + ]; +}; +``` - ```sh - deploy-to - ``` +Keep `domain` mandatory. +Nix does not own public DNS today. +The route domain still identifies the public route. +A future LAN DNS module can also consume it. -3. Check the game service. +The reverse-proxy module joins `registry.route` with `registry.address`. +It renders nginx `stream` entries for UDP ports. - ```sh - sudo systemctl status .service - sudo journalctl -u .service -n 200 --no-pager - ``` +```nginx +server { + listen 2456 udp; + proxy_pass 10.23.20.197:2456; +} +``` -4. Check UDP listeners. +Configure public DNS outside Nix: - ```sh - sudo ss -lunp | grep -E '||' - ``` +```text +valheim.alexion.dev -> public IP +``` -5. Connect from the game client. +Configure LAN DNS outside Nix: -The Valheim server is ready after the log contains `Game server connected`. -It can spend time generating a new world before it accepts connections. +```text +valheim.alexion.dev -> reverse-proxy guest IP +``` + +Configure router forwarding outside Nix: + +```text +2456/udp -> reverse-proxy guest:2456 +2457/udp -> reverse-proxy guest:2457 +``` + +## Stage 4: Move the server into a guest + +After the host-level game module works, add a guest module. +Use `configs/nixos/guests/.nix`. + +The guest module wraps `modules.services.games.`. +It also maps host paths into guest paths. + +For Valheim, the host config became: + +```nix +guests.valheim = { + enable = true; + vlan = 20; + address = "10.23.20.197"; + steamPath = "/pikachu/data/steamcmd"; + installPath = "/pikachu/data/servers/valheim/main"; + statePath = "/pikachu/media/games/servers/valheim/main"; + serverName = "main"; + reverseProxy = { + enable = true; + route = { + domain = "valheim.alexion.dev"; + udp.ports = [ + config.guests.valheim.port + (config.guests.valheim.port + 1) + ]; + }; + }; +}; +``` + +The generic guest machinery publishes registry entries. + +```nix +guests.valheim.address = "10.23.20.197"; +``` + +becomes: + +```nix +registry.address.pikachu.valheim = "10.23.20.197"; +``` + +The guest route becomes: + +```nix +registry.route.pikachu.valheim = config.guests.valheim.reverseProxy.route; +``` + +## Secrets + +Use SOPS for the game server password. +Use one shared secret for game servers: + +```yaml +services: + game-servers: + password: +``` + +The Valheim guest defaults to: + +```nix +passwordSecret = "services/game-servers/password"; +``` + +The host decrypts the secret. +The guest module bind-mounts the decrypted file into the guest. +The Valheim module reads the password file at runtime. + +Edit shared secrets with the host key: + +```sh +cd configs/nixos +sops-host secrets/shared.yaml +``` + +## Validation + +Evaluate the host: + +```sh +nix eval .#nixosConfigurations.pikachu.config.system.build.toplevel.drvPath --raw +``` + +Deploy the host: + +```sh +deploy-to pikachu +``` + +Check the host secret: + +```sh +sudo test -s /run/secrets/services/game-servers/password && echo secret-ok +``` + +Check the Valheim guest: + +```sh +sudo systemctl status container@valheim.service --no-pager +``` + +Check the guest secret: + +```sh +sudo nixos-container run valheim -- test -s /run/secrets/services/game-servers/password +``` + +Check the Valheim service: + +```sh +sudo nixos-container run valheim -- systemctl status valheim.service --no-pager +sudo nixos-container run valheim -- journalctl -u valheim.service -n 200 --no-pager +``` + +Wait for these log lines: + +```text +Game server connected +Opened Steam server +``` + +Check Valheim UDP listeners: + +```sh +sudo nixos-container run valheim -- ss -lunp | grep -E '2456|2457|valheim' +``` + +Check reverse-proxy UDP listeners: + +```sh +sudo nixos-container run reverse-proxy -- ss -lunp | grep -E '2456|2457|nginx' +``` + +Test the client through LAN DNS or public DNS: + +```text +valheim.alexion.dev:2456 +``` + +Test direct guest connectivity when ingress fails: + +```text +10.23.20.197:2456 +``` + +## Operational notes + +A first Valheim start can spend time generating a world. +The service accepts connections after `Opened Steam server` appears. + +SteamCMD can leave old ownership on migrated server files. +The SteamCMD module uses recursive tmpfiles repair for its managed directories.