From 63284459765b9630ca090e566a3368faefd22b58 Mon Sep 17 00:00:00 2001 From: alexion Date: Thu, 10 Sep 2026 10:19:01 -0400 Subject: [PATCH 1/5] 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. -- 2.47.3 From ab4f13a38556b9b6ba518e0d1d46ed21253373b9 Mon Sep 17 00:00:00 2001 From: alexion Date: Thu, 10 Sep 2026 10:20:56 -0400 Subject: [PATCH 2/5] docs: generalize SteamCMD server workflow --- docs/00 INBOX/ai/steamcmd servers.md | 84 ++++++++++++++++------------ 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/docs/00 INBOX/ai/steamcmd servers.md b/docs/00 INBOX/ai/steamcmd servers.md index 62d68d9..9b9e8da 100644 --- a/docs/00 INBOX/ai/steamcmd servers.md +++ b/docs/00 INBOX/ai/steamcmd servers.md @@ -1,12 +1,13 @@ # SteamCMD servers -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. +This note describes the workflow for adding a server that SteamCMD installs. +Start with the generic runner on a host. +Then move game behavior into a game module. +Move deployment behavior into a guest module after validation. -## Stage 1: Run one server with `steamcmd` +## Stage 1: Run the server with `steamcmd` -Start by proving the generic SteamCMD runner works on a host. +Prove the generic SteamCMD runner before adding game-specific modules. Configure `modules.services.steamcmd` directly in the host file. ```nix @@ -41,33 +42,41 @@ modules.services.steamcmd = { }; ``` -This layer owns SteamCMD update behavior and systemd wiring. -It also owns directory setup for SteamCMD metadata, server files, and state. +Use real values for the target game. +The example shows Valheim because it was the first server added. + +This layer owns shared SteamCMD behavior: + +- service user +- directory creation +- SteamCMD install and update command +- systemd service wrapper +- firewall ports Do not pin the game server version in Nix. SteamCMD mutates the installed server files. ## Stage 2: Add a game service module -After direct SteamCMD deployment works, create a game module. +Create a game module after direct SteamCMD deployment works. Use `configs/nixos/modules/services/games/.nix`. -The game module owns game-specific details: +The game module owns game-specific behavior: - Steam app ID - server executable -- required command-line flags -- documented UDP ports -- private or public listing policy +- command-line flags +- documented ports +- default listing policy -The host keeps deployment-specific values: +The host owns deployment-specific values: -- host paths +- paths - server name - secret choice -- guest placement, when a guest exists +- network placement -For Valheim, the host config became: +A host can run the game module directly during validation: ```nix modules.services.games.valheim = { @@ -76,17 +85,17 @@ modules.services.games.valheim = { installDir = "/pikachu/data/servers/valheim/main"; stateDir = "/pikachu/media/games/servers/valheim/main"; serverName = "main"; - password = "foobarbaz"; + passwordFile = "/run/secrets/services/game-servers/password"; }; ``` -The Valheim module translated that into `modules.services.steamcmd`. -It generated the Valheim argument list and UDP ports. +The game module translates its options into `modules.services.steamcmd`. +It passes the app ID, executable, arguments, and ports to the runner. ## Stage 3: Add UDP ingress -Valheim uses UDP. -HTTP virtual hosts do not route Valheim traffic. +Many game servers use UDP. +HTTP virtual hosts do not route raw UDP traffic. Extend `registry.route` with UDP ports: @@ -102,11 +111,11 @@ registry.route.pikachu.valheim = { Keep `domain` mandatory. Nix does not own public DNS today. -The route domain still identifies the public route. +The route domain identifies the public route. A future LAN DNS module can also consume it. The reverse-proxy module joins `registry.route` with `registry.address`. -It renders nginx `stream` entries for UDP ports. +It renders nginx `stream` entries for UDP routes. ```nginx server { @@ -134,15 +143,19 @@ Configure router forwarding outside Nix: 2457/udp -> reverse-proxy guest:2457 ``` +Use the game documentation to choose the port list. +For Valheim, the official guide says it uses the configured port and port plus one. + ## Stage 4: Move the server into a guest -After the host-level game module works, add a guest module. +Add a guest module after host-level validation succeeds. Use `configs/nixos/guests/.nix`. The guest module wraps `modules.services.games.`. -It also maps host paths into guest paths. +It maps host paths into guest paths. +It also connects guest address and route options to the registry. -For Valheim, the host config became: +The host config then owns deployment wiring: ```nix guests.valheim = { @@ -186,8 +199,8 @@ registry.route.pikachu.valheim = config.guests.valheim.reverseProxy.route; ## Secrets -Use SOPS for the game server password. -Use one shared secret for game servers: +Use SOPS for game server passwords. +Use one shared secret when servers can share a password: ```yaml services: @@ -203,7 +216,7 @@ 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. +The game module reads the password file at runtime. Edit shared secrets with the host key: @@ -232,7 +245,7 @@ Check the host secret: sudo test -s /run/secrets/services/game-servers/password && echo secret-ok ``` -Check the Valheim guest: +Check the guest: ```sh sudo systemctl status container@valheim.service --no-pager @@ -244,21 +257,22 @@ Check the guest secret: sudo nixos-container run valheim -- test -s /run/secrets/services/game-servers/password ``` -Check the Valheim service: +Check the game 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: +Wait for the game-specific ready log. +For Valheim, wait for these lines: ```text Game server connected Opened Steam server ``` -Check Valheim UDP listeners: +Check game UDP listeners: ```sh sudo nixos-container run valheim -- ss -lunp | grep -E '2456|2457|valheim' @@ -284,8 +298,8 @@ Test direct guest connectivity when ingress fails: ## Operational notes -A first Valheim start can spend time generating a world. -The service accepts connections after `Opened Steam server` appears. +A first server start can spend time preparing state. +For Valheim, first start can generate a world. SteamCMD can leave old ownership on migrated server files. The SteamCMD module uses recursive tmpfiles repair for its managed directories. -- 2.47.3 From 16c83313790a11977f54f3bdbcac817257a85c11 Mon Sep 17 00:00:00 2001 From: alexion Date: Thu, 10 Sep 2026 11:52:51 -0400 Subject: [PATCH 3/5] docs: trim SteamCMD workflow examples --- docs/00 INBOX/ai/steamcmd servers.md | 184 ++++----------------------- 1 file changed, 28 insertions(+), 156 deletions(-) diff --git a/docs/00 INBOX/ai/steamcmd servers.md b/docs/00 INBOX/ai/steamcmd servers.md index 9b9e8da..514f5b6 100644 --- a/docs/00 INBOX/ai/steamcmd servers.md +++ b/docs/00 INBOX/ai/steamcmd servers.md @@ -10,40 +10,8 @@ Move deployment behavior into a guest module after validation. Prove the generic SteamCMD runner before adding game-specific modules. Configure `modules.services.steamcmd` directly in the host file. -```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 - ]; -}; -``` - Use real values for the target game. -The example shows Valheim because it was the first server added. +For the first Valheim deployment, the direct configuration set the Steam app ID, install path, state path, executable, command-line arguments, and UDP ports directly on `modules.services.steamcmd`. This layer owns shared SteamCMD behavior: @@ -76,39 +44,16 @@ The host owns deployment-specific values: - secret choice - network placement -A host can run the game module directly during validation: - -```nix -modules.services.games.valheim = { - enable = true; - steamDir = "/pikachu/data/steamcmd"; - installDir = "/pikachu/data/servers/valheim/main"; - stateDir = "/pikachu/media/games/servers/valheim/main"; - serverName = "main"; - passwordFile = "/run/secrets/services/game-servers/password"; -}; -``` - -The game module translates its options into `modules.services.steamcmd`. -It passes the app ID, executable, arguments, and ports to the runner. +A host can run the game module directly during validation. +For Valheim, the host passed only deployment values to `modules.services.games.valheim`. +The game module translated those values into `modules.services.steamcmd` options. ## Stage 3: Add UDP ingress Many game servers use UDP. HTTP virtual hosts do not route raw UDP traffic. -Extend `registry.route` with UDP ports: - -```nix -registry.route.pikachu.valheim = { - domain = "valheim.alexion.dev"; - udp.ports = [ - 2456 - 2457 - ]; -}; -``` - +Extend `registry.route` with the UDP ports for the public route. Keep `domain` mandatory. Nix does not own public DNS today. The route domain identifies the public route. @@ -117,31 +62,9 @@ A future LAN DNS module can also consume it. The reverse-proxy module joins `registry.route` with `registry.address`. It renders nginx `stream` entries for UDP routes. -```nginx -server { - listen 2456 udp; - proxy_pass 10.23.20.197:2456; -} -``` - -Configure public DNS outside Nix: - -```text -valheim.alexion.dev -> public IP -``` - -Configure LAN DNS outside Nix: - -```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 -``` +Configure public DNS outside Nix to point the route domain at the public IP. +Configure LAN DNS outside Nix to point the same route domain at the reverse-proxy guest IP. +Configure router forwarding outside Nix to forward the documented UDP ports to the reverse-proxy guest. Use the game documentation to choose the port list. For Valheim, the official guide says it uses the configured port and port plus one. @@ -157,62 +80,20 @@ It also connects guest address and route options to the registry. The host config then owns deployment wiring: -```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) - ]; - }; - }; -}; -``` +- VLAN +- guest address +- host paths for SteamCMD files, server files, and server state +- server name +- reverse-proxy route -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; -``` +The generic guest machinery publishes `guests..address` as `registry.address..`. +It also publishes `guests..reverseProxy.route` as `registry.route..` when reverse proxying is enabled. ## Secrets Use SOPS for game server passwords. -Use one shared secret when servers can share a password: - -```yaml -services: - game-servers: - password: -``` - -The Valheim guest defaults to: - -```nix -passwordSecret = "services/game-servers/password"; -``` +Use one shared secret when servers can share a password. +The current shared key is `services/game-servers/password`. The host decrypts the secret. The guest module bind-mounts the decrypted file into the guest. @@ -227,16 +108,16 @@ sops-host secrets/shared.yaml ## Validation -Evaluate the host: +Evaluate the host before deployment: ```sh -nix eval .#nixosConfigurations.pikachu.config.system.build.toplevel.drvPath --raw +nix eval .#nixosConfigurations..config.system.build.toplevel.drvPath --raw ``` Deploy the host: ```sh -deploy-to pikachu +deploy-to ``` Check the host secret: @@ -248,20 +129,20 @@ sudo test -s /run/secrets/services/game-servers/password && echo secret-ok Check the guest: ```sh -sudo systemctl status container@valheim.service --no-pager +sudo systemctl status container@.service --no-pager ``` Check the guest secret: ```sh -sudo nixos-container run valheim -- test -s /run/secrets/services/game-servers/password +sudo nixos-container run -- test -s /run/secrets/services/game-servers/password ``` Check the game 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 +sudo nixos-container run -- systemctl status .service --no-pager +sudo nixos-container run -- journalctl -u .service -n 200 --no-pager ``` Wait for the game-specific ready log. @@ -275,26 +156,17 @@ Opened Steam server Check game UDP listeners: ```sh -sudo nixos-container run valheim -- ss -lunp | grep -E '2456|2457|valheim' +sudo nixos-container run -- ss -lunp | grep -E '|' ``` Check reverse-proxy UDP listeners: ```sh -sudo nixos-container run reverse-proxy -- ss -lunp | grep -E '2456|2457|nginx' +sudo nixos-container run reverse-proxy -- ss -lunp | grep -E '|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 -``` +Test the client through LAN DNS or public DNS. +Test direct guest connectivity when ingress fails. ## Operational notes -- 2.47.3 From 523655a9ec0cf94891990afe38651fac47809ac6 Mon Sep 17 00:00:00 2001 From: alexion Date: Thu, 10 Sep 2026 11:57:31 -0400 Subject: [PATCH 4/5] docs: make SteamCMD guide procedural --- docs/00 INBOX/ai/steamcmd servers.md | 187 +++++++++++++-------------- 1 file changed, 91 insertions(+), 96 deletions(-) diff --git a/docs/00 INBOX/ai/steamcmd servers.md b/docs/00 INBOX/ai/steamcmd servers.md index 514f5b6..7cae7c1 100644 --- a/docs/00 INBOX/ai/steamcmd servers.md +++ b/docs/00 INBOX/ai/steamcmd servers.md @@ -1,103 +1,107 @@ # SteamCMD servers -This note describes the workflow for adding a server that SteamCMD installs. -Start with the generic runner on a host. -Then move game behavior into a game module. -Move deployment behavior into a guest module after validation. +Use this workflow to add a server that SteamCMD installs. +Complete each stage before you start the next stage. -## Stage 1: Run the server with `steamcmd` +## Stage 1: Prove the SteamCMD runner -Prove the generic SteamCMD runner before adding game-specific modules. -Configure `modules.services.steamcmd` directly in the host file. +1. Add the server to the host with `modules.services.steamcmd`. +2. Set the Steam app ID. +3. Set the SteamCMD directory. +4. Set the server install directory. +5. Set the server state directory. +6. Set the server executable path. +7. Set the game arguments. +8. Set the documented game ports. +9. Evaluate the host configuration. +10. Deploy the host. +11. Start the service on the host. +12. Read the service logs. +13. Wait for the game-specific ready log. +14. Connect from the game client. -Use real values for the target game. -For the first Valheim deployment, the direct configuration set the Steam app ID, install path, state path, executable, command-line arguments, and UDP ports directly on `modules.services.steamcmd`. - -This layer owns shared SteamCMD behavior: - -- service user -- directory creation -- SteamCMD install and update command -- systemd service wrapper -- firewall ports +Keep this stage small. +Use it only to validate SteamCMD, game arguments, ports, and state paths. Do not pin the game server version in Nix. SteamCMD mutates the installed server files. -## Stage 2: Add a game service module +## Stage 2: Create the game module -Create a game module after direct SteamCMD deployment works. -Use `configs/nixos/modules/services/games/.nix`. +1. Create `configs/nixos/modules/services/games/.nix`. +2. Add the module to `configs/nixos/modules/default.nix`. +3. Define options under `modules.services.games.`. +4. Expose only deployment-specific values. +5. Keep host paths in the host configuration. +6. Keep secret selection in the host configuration. +7. Keep network placement outside the game module. +8. Move the Steam app ID into the game module. +9. Move the executable path into the game module. +10. Move game arguments into the game module. +11. Move documented ports into the game module. +12. Wire the game module to `modules.services.steamcmd`. +13. Replace the direct host `modules.services.steamcmd` config. +14. Enable `modules.services.games.` on the host. +15. Evaluate the host configuration. +16. Deploy the host. +17. Validate the service again on the host. -The game module owns game-specific behavior: - -- Steam app ID -- server executable -- command-line flags -- documented ports -- default listing policy - -The host owns deployment-specific values: - -- paths -- server name -- secret choice -- network placement - -A host can run the game module directly during validation. -For Valheim, the host passed only deployment values to `modules.services.games.valheim`. -The game module translated those values into `modules.services.steamcmd` options. +The game module owns game behavior. +The host owns deployment values. ## Stage 3: Add UDP ingress -Many game servers use UDP. -HTTP virtual hosts do not route raw UDP traffic. +1. Read the game's official server port documentation. +2. Add the UDP ports to `registry.route...udp.ports`. +3. Keep `registry.route...domain` set. +4. Point `registry.address..` at the backend address. +5. Evaluate the reverse-proxy host configuration. +6. Deploy the reverse-proxy host. +7. Verify nginx listens on the UDP ports. +8. Configure public DNS outside Nix. +9. Configure LAN DNS outside Nix. +10. Configure router forwarding outside Nix. +11. Test the public route from the game client. +12. Test the backend address directly when ingress fails. -Extend `registry.route` with the UDP ports for the public route. -Keep `domain` mandatory. -Nix does not own public DNS today. -The route domain identifies the public route. -A future LAN DNS module can also consume it. - -The reverse-proxy module joins `registry.route` with `registry.address`. -It renders nginx `stream` entries for UDP routes. - -Configure public DNS outside Nix to point the route domain at the public IP. -Configure LAN DNS outside Nix to point the same route domain at the reverse-proxy guest IP. -Configure router forwarding outside Nix to forward the documented UDP ports to the reverse-proxy guest. - -Use the game documentation to choose the port list. -For Valheim, the official guide says it uses the configured port and port plus one. +Use the route domain for identity only. +Nginx stream routing uses destination ports for UDP traffic. +It does not route UDP by hostname. ## Stage 4: Move the server into a guest -Add a guest module after host-level validation succeeds. -Use `configs/nixos/guests/.nix`. +1. Create `configs/nixos/guests/.nix`. +2. Add the guest module to `src/dotfiles/guests.nix`. +3. Wrap `modules.services.games.` inside the guest. +4. Bind-mount the host SteamCMD directory into the guest. +5. Bind-mount the host install directory into the guest. +6. Bind-mount the host state directory into the guest. +7. Use guest-local paths for the game module. +8. Add `guests.` to the host configuration. +9. Set the guest VLAN. +10. Set the guest address. +11. Enable reverse proxying when the server needs ingress. +12. Put the UDP route under `guests..reverseProxy.route`. +13. Evaluate the host configuration. +14. Deploy the host. +15. Validate the container service. +16. Validate the game service inside the guest. +17. Validate reverse-proxy UDP listeners. +18. Connect from the game client. -The guest module wraps `modules.services.games.`. -It maps host paths into guest paths. -It also connects guest address and route options to the registry. - -The host config then owns deployment wiring: - -- VLAN -- guest address -- host paths for SteamCMD files, server files, and server state -- server name -- reverse-proxy route - -The generic guest machinery publishes `guests..address` as `registry.address..`. -It also publishes `guests..reverseProxy.route` as `registry.route..` when reverse proxying is enabled. +The guest machinery publishes `guests..address` to `registry.address..`. +It publishes `guests..reverseProxy.route` to `registry.route..`. ## Secrets -Use SOPS for game server passwords. -Use one shared secret when servers can share a password. -The current shared key is `services/game-servers/password`. - -The host decrypts the secret. -The guest module bind-mounts the decrypted file into the guest. -The game module reads the password file at runtime. +1. Use SOPS for game server passwords. +2. Use `services/game-servers/password` for a shared game-server password. +3. Add a per-game key only when the game needs a different password. +4. Decrypt the secret on the host. +5. Bind-mount the decrypted secret into the guest. +6. Pass the password file to the game module. +7. Read the password file at service start. +8. Do not put plaintext passwords in host configuration. Edit shared secrets with the host key: @@ -106,9 +110,9 @@ cd configs/nixos sops-host secrets/shared.yaml ``` -## Validation +## Validation commands -Evaluate the host before deployment: +Evaluate the host: ```sh nix eval .#nixosConfigurations..config.system.build.toplevel.drvPath --raw @@ -145,14 +149,6 @@ sudo nixos-container run -- systemctl status .service --no-pager sudo nixos-container run -- journalctl -u .service -n 200 --no-pager ``` -Wait for the game-specific ready log. -For Valheim, wait for these lines: - -```text -Game server connected -Opened Steam server -``` - Check game UDP listeners: ```sh @@ -165,13 +161,12 @@ Check reverse-proxy UDP listeners: sudo nixos-container run reverse-proxy -- ss -lunp | grep -E '|nginx' ``` -Test the client through LAN DNS or public DNS. -Test direct guest connectivity when ingress fails. +## Operational checks -## Operational notes +1. Watch the first server start closely. +2. Expect some games to initialize state on first start. +3. Wait for the game-specific ready log before client testing. +4. Check SteamCMD logs when updates stall. +5. Check directory ownership when SteamCMD reports write failures. -A first server start can spend time preparing state. -For Valheim, first start can generate a world. - -SteamCMD can leave old ownership on migrated server files. -The SteamCMD module uses recursive tmpfiles repair for its managed directories. +The SteamCMD module repairs ownership recursively for managed directories. -- 2.47.3 From cee0673ae8a90109bd23b86a77ffa91ea80bee05 Mon Sep 17 00:00:00 2001 From: alexion Date: Thu, 10 Sep 2026 11:59:41 -0400 Subject: [PATCH 5/5] docs: clarify SteamCMD module workflow --- docs/00 INBOX/ai/steamcmd servers.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/00 INBOX/ai/steamcmd servers.md b/docs/00 INBOX/ai/steamcmd servers.md index 7cae7c1..60f951f 100644 --- a/docs/00 INBOX/ai/steamcmd servers.md +++ b/docs/00 INBOX/ai/steamcmd servers.md @@ -29,19 +29,19 @@ SteamCMD mutates the installed server files. ## Stage 2: Create the game module 1. Create `configs/nixos/modules/services/games/.nix`. -2. Add the module to `configs/nixos/modules/default.nix`. -3. Define options under `modules.services.games.`. -4. Expose only deployment-specific values. -5. Keep host paths in the host configuration. -6. Keep secret selection in the host configuration. -7. Keep network placement outside the game module. -8. Move the Steam app ID into the game module. -9. Move the executable path into the game module. -10. Move game arguments into the game module. -11. Move documented ports into the game module. -12. Wire the game module to `modules.services.steamcmd`. -13. Replace the direct host `modules.services.steamcmd` config. -14. Enable `modules.services.games.` on the host. +2. Define options under `modules.services.games.`. +3. Expose only deployment-specific values. +4. Keep host paths in the host configuration. +5. Keep secret selection in the host configuration. +6. Keep network placement outside the game module. +7. Move the Steam app ID into the game module. +8. Move the executable path into the game module. +9. Move game arguments into the game module. +10. Move documented ports into the game module. +11. Wire the game module to `modules.services.steamcmd`. +12. Replace the direct host `modules.services.steamcmd` config. +13. Enable `modules.services.games.` on the host. +14. Stage the new module file. 15. Evaluate the host configuration. 16. Deploy the host. 17. Validate the service again on the host. -- 2.47.3