docs: update SteamCMD server workflow #38

Closed
alexion wants to merge 5 commits from docs/steamcmd-server-workflow into main

View File

@@ -1,71 +1,172 @@
# SteamCMD servers # SteamCMD servers
Use `modules.services.steamcmd` as the low-level runner for one SteamCMD server. Use this workflow to add a server that SteamCMD installs.
Create a game module under `configs/nixos/modules/services/games/` for each game. Complete each stage before you start the next stage.
The game module owns game-specific defaults and command-line flags. ## Stage 1: Prove the SteamCMD runner
The host owns deployment-specific paths, names, secrets, and network placement.
## Create a game module 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.
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: Create the game module
1. Create `configs/nixos/modules/services/games/<game>.nix`. 1. Create `configs/nixos/modules/services/games/<game>.nix`.
2. Define options under `modules.services.games.<game>`. 2. Define options under `modules.services.games.<game>`.
3. Expose only values the host must choose. 3. Expose only deployment-specific values.
4. Map those values to `modules.services.steamcmd`. 4. Keep host paths in the host configuration.
5. Set the Steam dedicated-server app ID in the game module. 5. Keep secret selection in the host configuration.
6. Set the server executable in the game module. 6. Keep network placement outside the game module.
7. Generate game-specific arguments in the game module. 7. Move the Steam app ID into the game module.
8. Open the game's documented UDP ports in 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.<game>` 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.
Keep `modules.services.steamcmd` focused on shared SteamCMD behavior. The game module owns game behavior.
Do not put game-specific flags or port rules there. The host owns deployment values.
## Configure a host ## Stage 3: Add UDP ingress
During validation, enable the game module directly on a host. 1. Read the game's official server port documentation.
2. Add the UDP ports to `registry.route.<host>.<name>.udp.ports`.
3. Keep `registry.route.<host>.<name>.domain` set.
4. Point `registry.address.<host>.<name>` 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.
```nix Use the route domain for identity only.
modules.services.games.valheim = { Nginx stream routing uses destination ports for UDP traffic.
enable = true; It does not route UDP by hostname.
steamDir = "/pikachu/data/steamcmd";
installDir = "/pikachu/data/servers/valheim/main"; ## Stage 4: Move the server into a guest
stateDir = "/pikachu/media/games/servers/valheim/main";
serverName = "main"; 1. Create `configs/nixos/guests/<game>.nix`.
password = "foobarbaz"; 2. Add the guest module to `src/dotfiles/guests.nix`.
}; 3. Wrap `modules.services.games.<game>` 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.<name>` 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.<name>.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 machinery publishes `guests.<name>.address` to `registry.address.<host>.<name>`.
It publishes `guests.<name>.reverseProxy.route` to `registry.route.<host>.<name>`.
## Secrets
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:
```sh
cd configs/nixos
sops-host secrets/shared.yaml
``` ```
Use host paths for direct host deployment. ## Validation commands
Use guest paths after the server moves into a guest.
## Deploy and test Evaluate the host:
1. Evaluate the host configuration. ```sh
nix eval .#nixosConfigurations.<host>.config.system.build.toplevel.drvPath --raw
```
```sh Deploy the host:
nix eval .#nixosConfigurations.<host>.config.system.build.toplevel.drvPath --raw
```
2. Deploy the host. ```sh
deploy-to <host>
```
```sh Check the host secret:
deploy-to <host>
```
3. Check the game service. ```sh
sudo test -s /run/secrets/services/game-servers/password && echo secret-ok
```
```sh Check the guest:
sudo systemctl status <game>.service
sudo journalctl -u <game>.service -n 200 --no-pager
```
4. Check UDP listeners. ```sh
sudo systemctl status container@<guest>.service --no-pager
```
```sh Check the guest secret:
sudo ss -lunp | grep -E '<port>|<port-plus-one>|<game>'
```
5. Connect from the game client. ```sh
sudo nixos-container run <guest> -- test -s /run/secrets/services/game-servers/password
```
The Valheim server is ready after the log contains `Game server connected`. Check the game service:
It can spend time generating a new world before it accepts connections.
```sh
sudo nixos-container run <guest> -- systemctl status <game>.service --no-pager
sudo nixos-container run <guest> -- journalctl -u <game>.service -n 200 --no-pager
```
Check game UDP listeners:
```sh
sudo nixos-container run <guest> -- ss -lunp | grep -E '<ports>|<game>'
```
Check reverse-proxy UDP listeners:
```sh
sudo nixos-container run reverse-proxy -- ss -lunp | grep -E '<ports>|nginx'
```
## Operational checks
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.
The SteamCMD module repairs ownership recursively for managed directories.