Managed dedicatedFactorio
A KBVE-baked Factorio dedicated-server image built to run as an Agones GameServer on the production Kubernetes cluster — a thin layer over the factoriotools rootless base with an in-tree scenario and a lifecycle shim.
Agones lifecycle shim
shim/agones-shim.sh execs the Factorio binary, seeds config defaults when no ConfigMap is mounted, drives Ready() plus a periodic Health() heartbeat, and traps SIGTERM so shutdowns get a clean autosave.
- Rootless — runs as non-root UID 845.
- Local-friendly — SDK skipped when AGONES_SDK_HTTP unset.
What it gives you
Features
Agones lifecycle shim
Ready() / periodic Health() and SIGTERM-driven autosave on shutdown.
KBVE scenario
An in-tree Lua soft-mod layer — joins, donor perks, chat commands — redmew-inspired but written fresh.
RCON + console-log
Loopback RCON and --console-log feed the planned chat-relay sidecar over a shared volume.
Rootless
Runs as non-root UID 845 so the GameServer PodSpec can require runAsNonRoot without a fight.
How it fits together
Overview
KBVE-baked Factorio dedicated-server image, designed to run as an Agones GameServer on the production Kubernetes cluster (issue #11138).
The image is a thin layer on top of factoriotools/factorio:2.0.76-rootless:
- Lifecycle shim —
shim/agones-shim.shexecs the Factorio binary, copies bakeable defaults into/factorio/config/only when a ConfigMap mount hasn’t already provided the file, drivesReady()+ a periodicHealth()heartbeat whenAGONES_SDK_HTTPis set, and trapsSIGTERMso Agones-initiated shutdowns get a clean autosave. - KBVE scenario —
scenarios/kbve/is our own Lua scenario, redmew-inspired but written fresh so we can grow the soft-mod layer in-tree (join/leave broadcasts, donor perks, chat commands). - RCON + console-log plumbing — when
FACTORIO_RCON_PASSWORDis set the shim opens RCON bound to127.0.0.1:27015(loopback-only by default) and passes--console-logso the planned chat-relay sidecar can stream[CHAT]/[JOIN]/[LEAVE]/[COMMAND]markers off a sharedemptyDirvolume. - Rootless — UID 845
factorio, so the eventualGameServerPodSpec can requirerunAsNonRoot: truewithout a SecurityContext fight.
Roadmap
Phasing
| Phase | Scope |
|---|---|
| 0 | Custom image + scenario + lifecycle shim (this entry) |
| 1 | apps/kube/agones/factorio/ GameServer/Fleet + UDP exposure |
| 2 | ConfigMap-driven server-settings.json + ExternalSecret for matchmaking token and RCON password |
| 3 | Chat-relay sidecar (apps/agones/factorio/relay/, Rust) — log tail + RCON + IRC client; IRC → existing Discord bridge |
| 4 | PVC + save-rotation CronJob + public download URL |
| 5 | Observability — log shipping + RCON metrics + UPS / player-count alerts |
Configuration
Container & runtime knobs
kbve/agones-factorio (local) / ghcr.io/kbve/agones-factorio (production), built via the standard nx container target:
./kbve.sh -nx agones-factorio:container| Env | Default | Notes |
|---|---|---|
FACTORIO_SCENARIO | kbve | Scenario directory under /factorio/scenarios/ |
FACTORIO_PORT | 34197 | UDP listen port |
FACTORIO_CONFIG_DIR | /factorio/config | Mount a ConfigMap here in Phase 2 |
FACTORIO_CONSOLE_LOG | /shared/log/console.log | Tailable by the Phase 3 relay sidecar |
FACTORIO_RCON_PORT | 27015 | TCP, loopback-only by default |
FACTORIO_RCON_BIND | 127.0.0.1 | RCON listen address |
FACTORIO_RCON_PASSWORD | (unset) | When unset, RCON is not opened |
AGONES_SDK_HTTP | (unset) | When unset, SDK calls are skipped — local docker run works without Agones |
AGONES_READY_DELAY | 30 | Seconds before Ready() |
AGONES_HEALTH_INTERVAL | 5 | Health() cadence |
References — Issue #11138 (original planning ticket) · factoriotools/factorio (upstream rootless image) · Agones GameServer CRD.
Questions
Frequently asked
What is the Agones Factorio image?
A KBVE-baked Factorio dedicated-server OCI image built to run as an Agones GameServer on the production Kubernetes cluster, layered on the factoriotools rootless base.
How does the Agones lifecycle shim work?
The shim execs the Factorio binary, seeds config defaults when no ConfigMap is mounted, drives Ready() plus a periodic Health() heartbeat, and traps SIGTERM so shutdowns get a clean autosave.
Can the image run locally without Agones?
Yes. When AGONES_SDK_HTTP is unset the SDK calls are skipped, so a plain docker run works without an Agones controller.
