Skip to content
agones · factorio · rust · docker

Agones control API forFactorio

A Rust Axum + utoipa REST API for the Agones-managed Factorio dedicated server — reads GameServer status via kube-rs and ClickHouse-backed player telemetry, surfaced through the staff dashboard over a same-origin proxy.

Player count from ClickHouse

Player count comes from ClickHouse (gameops.factorio_snapshots), not RCON — RCON binds loopback-only on the gameserver pod and is unreachable cross-pod. The relay sidecar produces telemetry; factorio-ctl reads the latest snapshot.

  • Prefix — routes use /factorio/*, never /fc/*.
  • Status — GameServer read via kube-rs, no CRD vendoring.
factorio-ctlApp
Axum + utoipaStack
9002Listen
ClickHouseTelemetry

What it gives you

Features

GameServer status

Reads Agones GameServer state / node / address / port via kube-rs with a raw request, so an Agones schema bump can't break the build.

ClickHouse telemetry

Player count from gameops.factorio_snapshots, produced by the relay sidecar — RCON binds loopback-only and is unreachable cross-pod.

Same-origin proxy

Cluster-internal only, surfaced through the staff dashboard via /dashboard/factorio/proxy under DASHBOARD_VIEW.

OpenAPI spec

Serves a utoipa /openapi.json for the dashboard Scalar viewer.

Responsibilities

Overview & API

Rust Axum + utoipa REST API for the Agones-managed Factorio dedicated server. Mirrors firecracker-ctl: cluster-internal only, surfaced through the staff dashboard via a same-origin proxy (/dashboard/factorio/proxy, DASHBOARD_VIEW). Routes use the /factorio/* prefix — never /fc/*, which belongs to firecracker-ctl.

This entry registers the crate so the image builds and publishes. The Kubernetes Deployment ships with replicas: 0 until the image is available, then scales to 1.

MethodPathDescription
GET/factorio/health (+ /health)Liveness, build identity, in-cluster kube-client presence
GET/factorio/serverAgones GameServer status (state / node / address / port) + latest telemetry snapshot
GET/openapi.jsonutoipa spec for the dashboard Scalar viewer
  • GameServer status is read via kube-rs with a raw request to the Agones API (no CRD vendoring), so an Agones schema bump can’t break the build.
  • Player count comes from ClickHouse (gameops.factorio_snapshots), not RCON — RCON binds loopback-only on the gameserver pod and is unreachable cross-pod. The relay sidecar is the telemetry producer; factorio-ctl reads the latest snapshot.

Config & deploy

Runtime & Kubernetes

EnvDefaultNotes
FACTORIO_CTL_PORT9002Listen port
FACTORIO_NAMESPACEfactorioGameServer namespace
FACTORIO_GAMESERVERfactorioGameServer name
FACTORIO_SERVER_IDfactorio-1Stamps telemetry lookups
CLICKHOUSE_ENDPOINT(unset)When unset, telemetry fields are omitted from /factorio/server
CLICKHOUSE_USER / CLICKHOUSE_PASSWORD(unset)From the clickhouse-credentials ExternalSecret
CLICKHOUSE_DATABASEgameops

In apps/kube/agones/factorio/manifests/factorio-ctl.yaml:

  • Deploymentreplicas: 0 until the image publishes, then scale to 1
  • Service — ClusterIP on port 9002
  • RBAC — ServiceAccount + Role granting get/list/watch on agones.dev/gameservers
  • Mutating endpoints: /factorio/server/restart · /factorio/server/save · /factorio/saves/* · /factorio/rotation/* · /factorio/rcon/exec · /factorio/players.
  • Dashboard “control” tab consuming factorio-ctl.
  • Tracking issue #12735 (supersedes #11138)
  • Sibling pattern — firecracker-ctl

Questions

Frequently asked

What is factorio-ctl?

A Rust Axum plus utoipa REST API for the Agones-managed Factorio dedicated server, exposing GameServer status and telemetry to the staff dashboard through a same-origin proxy.

Where does the player count come from?

From ClickHouse (gameops.factorio_snapshots), not RCON — RCON binds loopback-only on the gameserver pod and is unreachable cross-pod, so the relay sidecar produces telemetry and factorio-ctl reads the latest snapshot.

How does it read GameServer status without breaking on Agones upgrades?

It uses kube-rs with a raw request to the Agones API rather than vendoring the CRD, so an Agones schema bump cannot break the build.