docs: update SteamCMD server workflow #38
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user