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