Files

110 lines
5.7 KiB
Markdown
Raw Permalink Normal View History

# Factorio team server
Achievement-safe Factorio 2.0.77 on Kubernetes. Public upstream images, a fresh
Ceph RBD world, Prometheus metrics, and the provisioned Grafana dashboard:
https://grafana.brunner.ninja/d/factorio-server
Connect on the LAN to `192.168.0.17:34197` (UDP). The existing WAN UDP
forwarding on `newgw` is retained.
Offline/unverified accounts are allowed (`require_user_verification: false`).
The world uses the base game, without Space Age or gameplay mods. No Lua console
commands are issued. The player collector only uses `/players online`, which
preserves achievements. Production, science, UPS and other Lua-only statistics
are intentionally excluded. The server pauses when empty; autosaves occur every
five minutes with ten rotating slots. Public server-list registration is off.
## Placement and maintenance
Factorio uses the same scheduling model as Garry's Mod: node affinity gives
**arschrock** the highest preference, while the scheduler may use an Odroid if
arschrock is unavailable, cordoned, or cannot satisfy the Pod's resource
request. There is no custom placement controller or periodic placement Pod.
Kubernetes does not automatically move an already-running Pod back merely
because arschrock later becomes preferable again.
Readiness and startup probes check authenticated RCON, the save directory and
its writability; liveness allows five minutes for slow saves before restarting
a hung game.
**Current infrastructure limitation:** arschrock is the sole API server,
scheduler and etcd host. A complete outage cannot trigger rescheduling until
the control plane is reachable. No control-plane configuration is changed here.
Ceph must also be healthy and able to detach/fence the old RBD attachment before
a new writer can start. This project never force-deletes pods or attachments to
bypass storage safety.
## Deploy and upgrade
Requirements: kubectl, Python 3 with PyYAML, and the configured homelab context.
`bootstrap.sh` is the one-time administrator path: namespace, limited RBAC,
random RCON secret, PVC and initial resources. Run `install.sh` thereafter.
Both work from any directory, validate on the API before applying, and wait for
rollout. Routine installs leave PVCs untouched.
Change the server image in **factorio.yaml** and commit to **main**. Kustomize
copies that one image value to the player collector. The pipeline validates
manifests, validates the arschrock scheduling preference, starts the actual server with the actual
rendered scripts/settings, tests probes and save/restart persistence, then
applies only the pre-created named resources. No custom game image is built.
The dashboard and scripts are deployed in the same pipeline. Configuration
checksums trigger a restart when mounted game scripts/settings change.
Recreate prevents simultaneous old/new servers. Shutdown requests a save and
allows 180 seconds. Startup copies existing save ZIPs into `upgrade-backups/`
on the same PVC before the new binary loads them, retaining five startup
snapshots. This protects against upgrade conversion but is **not** an off-volume
backup. No automatic downgrade is attempted: older game binaries may not load a
newer save. To roll back, stop the server and restore an appropriate snapshot,
then deploy the matching older image.
The original default/factorio-pvc world and its backing PV were explicitly
retired on 2026-10-17. Fresh data lives in factorio/factorio-data (16 GiB).
Routine install/CD never deletes or recreates this storage.
## CI access
Create `KUBE_CONFIG_BASE64` from `./create-ci-kubeconfig.sh` and upload its output
directly as a Gitea Actions repository secret; never commit it. The credential
belongs to `system:serviceaccount:factorio:factorio-deployer`, not an administrator.
It can get/patch/update/watch only the named Deployment, Services, ConfigMaps
and monitoring resources, read pod/event diagnostics in the isolated
factorio namespace, and update only `default/factorio-dashboard`. It cannot read
or list Secrets, modify PVCs, delete resources, modify nodes or grant RBAC.
Bootstrap creates these objects because resourceNames cannot constrain create.
The runner currently reaches the API at host.containers.internal:6443 with TLS
server name 192.168.0.2, matching the other homelab pipelines. Update that CI
endpoint when the control plane moves. Local commands use the existing context
without kubeconfig or context overrides.
## Monitoring and tests
The Grafana dashboard shows RAM, CPU, player count, RCON responsiveness, disk
space, network throughput, restarts, active host, and scrape latency. Metrics
are a textfile collector served by upstream node-exporter. Failed RCON queries
report server_up=0 and omit player counts rather than fabricate zero players.
Prometheus alerts cover unavailable server/exporter, stale player collection,
restarts and nearly full save storage.
Neither Service exposes RCON. Its random password is stored only in a Secret
and the server's private config file; upstream shell tracing is suppressed.
```sh
./test.sh
FACTORIO_SMOKE_TEST=true ./test.sh # requires Docker
```
Tests consume the rendered deployment; they do not maintain a second copy of
its image, environment, scripts or server settings.
2026-10-17 16:30:34 +02:00
## OLSR / Funkfeuer access
Connect directly to **10.12.4.170:34197/UDP** from a client with an OLSR route
to dahoam (arschrock). The Service declares this existing host address in
`externalIPs`; Cilium forwards traffic from the funkfeuer interface to the
current ready game pod. No socat process or additional host NAT rules are used.
The existing internet forwarding and LAN address remain available.
The client must actually route 10.12.4.170 through OLSR; this setting cannot
force the client's route or guarantee mesh latency. The entry point requires
dahoam to be online even when the game pod is on an Odroid.