docs: make SteamCMD guide procedural
This commit is contained in:
@@ -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/<game>.nix`.
|
||||
1. Create `configs/nixos/modules/services/games/<game>.nix`.
|
||||
2. Add the module to `configs/nixos/modules/default.nix`.
|
||||
3. Define options under `modules.services.games.<game>`.
|
||||
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.<game>` 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.<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.
|
||||
|
||||
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/<game>.nix`.
|
||||
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 module wraps `modules.services.games.<game>`.
|
||||
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.<name>.address` as `registry.address.<host>.<name>`.
|
||||
It also publishes `guests.<name>.reverseProxy.route` as `registry.route.<host>.<name>` when reverse proxying is enabled.
|
||||
The guest machinery publishes `guests.<name>.address` to `registry.address.<host>.<name>`.
|
||||
It publishes `guests.<name>.reverseProxy.route` to `registry.route.<host>.<name>`.
|
||||
|
||||
## 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.<host>.config.system.build.toplevel.drvPath --raw
|
||||
@@ -145,14 +149,6 @@ sudo nixos-container run <guest> -- systemctl status <game>.service --no-pager
|
||||
sudo nixos-container run <guest> -- journalctl -u <game>.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 '<ports>|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.
|
||||
|
||||
Reference in New Issue
Block a user