You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Correction to my original framing (thanks @jessica38 for pushing on it): I wrote that the registry already amounts to dynamic service discovery, and that is only half true. Checked against BackendRouter:
Initial joins are config-driven. The candidate pool comes from [servers] and the try order in nimbus.proxy.toml; the registry snapshot is used to filter that pool for health (stale, maintenance, capacity), never to add to it. A backend that heartbeats but is absent from the config is not a candidate for a player connecting to the proxy.
Transfers are registry-driven./server <id>, /nimbus send and intents resolve the target from the registry snapshot, so those can reach a backend that only exists in the registry.
So the explicit Velocity-shaped model (a config file listing servers and a fallback order) is already the primary one, and it should stay that way. What this issue asks for is creating and destroying containers; it must be an opt-in layer on top of that model, not a replacement for it.
Proposal
A separate provisioner process (not a proxy plugin: cloud/orchestrator credentials stay out of the player-facing process), driving world templates declared in config: image or egg, variables, resource limits, savegame persistence policy.
Driver 1: Docker. Talks to the Docker socket; spawning a backend is exactly what the egg install already does, containerized. Smallest useful increment.
Driver 2: Kubernetes operator. A NimbusBackend CRD reconciled into StatefulSet + PVC per world. No k8s Services needed for discovery, heartbeats already do it.
Flagship use case: ephemeral event/minigame worlds that exist only while players are on them, later auto-scale rules (spawn when the hub queue grows, despawn when empty for N minutes).
Open questions: savegame lifecycle for ephemeral worlds (discard vs archive), secret distribution to spawned containers (inject at spawn, never bake into templates), who is allowed to spawn (admin socket permission model already has scopes), and how templates relate to the existing eggs (same install scripts, ideally).
Relates to #41 (evacuate is the safe-despawn primitive) and the panel eggs, which are effectively the manual version of a template.
Scale-to-zero: pause idle worlds
Nimbus has the exact signal needed: heartbeats carry player counts and the proxy sees every session, so "this world is empty" is known with certainty. Tiered lifecycle per template policy:
Warm pause (docker pause / SIGSTOP) after N idle minutes: zero CPU, RAM kept, resume in milliseconds. A frozen process's listening socket still accepts connections into its backlog, so wake-on-connect is genuinely feasible: the proxy dials, the provisioner unfreezes, the connection completes.
Cold stop (docker stop) after M idle hours: frees the RAM too (the real cost of a VS server); resume is 30-60s since worldgen already happened (the 2-4 min initial generation is paid once).
Archive: container deleted, savegame kept. For finished event worlds.
Design point that needs care: a frozen backend stops heartbeating, so the registry would mark it stale and the proxy would refuse to route to it. This needs a provisioner-set Paused state in the registry, distinct from stale/dead, with "routable after wake" semantics: a route request toward a paused world triggers the wake, then routes, instead of rejecting.
Correction to my original framing (thanks @jessica38 for pushing on it): I wrote that the registry already amounts to dynamic service discovery, and that is only half true. Checked against
BackendRouter:[servers]and thetryorder innimbus.proxy.toml; the registry snapshot is used to filter that pool for health (stale, maintenance, capacity), never to add to it. A backend that heartbeats but is absent from the config is not a candidate for a player connecting to the proxy./server <id>,/nimbus sendand intents resolve the target from the registry snapshot, so those can reach a backend that only exists in the registry.So the explicit Velocity-shaped model (a config file listing servers and a fallback order) is already the primary one, and it should stay that way. What this issue asks for is creating and destroying containers; it must be an opt-in layer on top of that model, not a replacement for it.
Proposal
NimbusBackendCRD reconciled into StatefulSet + PVC per world. No k8s Services needed for discovery, heartbeats already do it.nimctl spawn <template> [--name events-3]andnimctl despawn <server>= drain, evacuate (Evacuate: move every player off a backend in one command #41), then delete. Scale-down is only safe because those primitives exist.Open questions: savegame lifecycle for ephemeral worlds (discard vs archive), secret distribution to spawned containers (inject at spawn, never bake into templates), who is allowed to spawn (admin socket permission model already has scopes), and how templates relate to the existing eggs (same install scripts, ideally).
Relates to #41 (evacuate is the safe-despawn primitive) and the panel eggs, which are effectively the manual version of a template.
Scale-to-zero: pause idle worlds
Nimbus has the exact signal needed: heartbeats carry player counts and the proxy sees every session, so "this world is empty" is known with certainty. Tiered lifecycle per template policy:
docker pause/ SIGSTOP) after N idle minutes: zero CPU, RAM kept, resume in milliseconds. A frozen process's listening socket still accepts connections into its backlog, so wake-on-connect is genuinely feasible: the proxy dials, the provisioner unfreezes, the connection completes.docker stop) after M idle hours: frees the RAM too (the real cost of a VS server); resume is 30-60s since worldgen already happened (the 2-4 min initial generation is paid once).Design point that needs care: a frozen backend stops heartbeating, so the registry would mark it stale and the proxy would refuse to route to it. This needs a provisioner-set
Pausedstate in the registry, distinct from stale/dead, with "routable after wake" semantics: a route request toward a paused world triggers the wake, then routes, instead of rejecting.