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
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.
Use this workflow to add a server that SteamCMD installs.
Complete each stage before you start the next stage.
The game module owns game-specific defaults and command-line flags.
The host owns deployment-specific paths, names, secrets, and network placement.
## Stage 1: Prove the SteamCMD runner
## 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`.
2. Define options under `modules.services.games.<game>`.
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.
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.<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.
Do not put game-specific flags or port rules there.
The game module owns game behavior.
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
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";
password = "foobarbaz";
};
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
1. Create `configs/nixos/guests/<game>.nix`.
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.
Use guest paths after the server moves into a guest.
## Validation commands
## Deploy and test
Evaluate the host:
1. Evaluate the host configuration.
```sh
nix eval .#nixosConfigurations.<host>.config.system.build.toplevel.drvPath --raw
```
```sh
nix eval .#nixosConfigurations.<host>.config.system.build.toplevel.drvPath --raw
```
Deploy the host:
2. Deploy the host.
```sh
deploy-to <host>
```
```sh
deploy-to <host>
```
Check the host secret:
3. Check the game service.
```sh
sudo test -s /run/secrets/services/game-servers/password && echo secret-ok
```
```sh
sudo systemctl status <game>.service
sudo journalctl -u <game>.service -n 200 --no-pager
```
Check the guest:
4. Check UDP listeners.
```sh
sudo systemctl status container@<guest>.service --no-pager
```
```sh
sudo ss -lunp | grep -E '<port>|<port-plus-one>|<game>'
```
Check the guest secret:
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`.
It can spend time generating a new world before it accepts connections.
Check the game service:
```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.