PlanyDocs
Playpute

Autoscaling

A fleet of identical game servers that grows and shrinks with demand.

projects/{project}/locations/{location}/gameServerGroups/{group}

The shape a minigame needs and a single server cannot be: a lobby fills and more servers arrive, a round ends and they go away. You declare a game, a size range and what to scale on; the group creates and removes servers to match.

Autoscaling → New group → choose a game, a machine type and a size range → Create group

Members are ordinary game servers

A member is a game server with group set, and everything that works on a server works on it — read it, read its logs, see who is playing on it.

Deleting a member by hand does not stick. The group's size is the group's to decide, so it creates another. Pause the group, or lower max_size.

Everything about a member — the game, the build, the machine type, whether it runs on spot, whether it keeps data, whether players can reach it, which private network it joins — is declared once on the group, so members never differ from one another.

FieldCan it change?
policy, display_name, sleep_after_idleYes, at once.
settingsYes; members created afterwards get the new settings.
machine_type, spotYes; each member picks the change up the next time it starts.
buildYes, as a rolling update.
game, ephemeral, exposure, vpcNo — create a new group.

The group itself is free

You pay for its members, billed exactly as any other game server, and for its schematic, billed as storage. The group costs nothing.

Warm-up

A group's first act is to create one server, let it install and generate its world for warmup_duration, capture it as a schematic, and remove it. Every member then starts from that capture — seconds rather than minutes.

The warm-up server is not charged for. It exists so your own members do not each repeat the same install.

warmup_duration is really "how long does my game take to be ready to play", and getting it wrong shows in one direction only: too long wastes a few minutes once, and too short captures a server mid-install, which every member then starts from. The clock starts when the warm-up server is running, not when it is created.

Zero skips warm-up and members start from scratch — right for a game that starts in seconds anyway.

If a capture fails, the group still scales; its members simply start from scratch, more slowly. To reuse a schematic you already have, set schematic when creating the group — see Giving one to a group.

Scaling policy

Every term is a number to hold, not a threshold to cross.

FieldWhat it does
min_size / max_sizeThe range. max_size is required: it is the ceiling on members, and so on what the fleet can cost.
target_player_percentHold members at this fraction of their player capacity, 1 to 100. Needs the game to declare max_players; without it, this term is ignored rather than guessed at.
min_free_player_slotsKeep this many player slots free across the group, so a party arriving together finds room without waiting. Adds to the percentage.
scale_on_waiting_connectionsAdd a member when somebody is connecting and nobody is playing yet. The only signal that sees demand before it lands.
target_cpu_percentHold the group's average CPU at this percentage of each member's machine type, 1 to 100.
buffer_size / buffer_percentKeep this many members ready and unallocated on top of the allocated ones — see Allocation.
scale_out_cooldownHow long after growing before the group may grow again.
scale_in_patienceHow long a member must have been empty before it may be removed.
max_scale_out_batch / max_scale_in_batchMost members to add or remove in one step.
drain_timeoutHow long a draining member is given to empty before it is removed anyway.

Zero means the term is off — not "target zero".

The terms combine by maximum: each answers "how many members does this load need", and the largest answer is taken.

min_size: 0 is legitimate, and is what a group for an off-peak game wants: no players, no servers, no bill. The first player then waits for a member to start, which is what the schematic is for.

The CLI sets --min-size, --max-size, --target-player-percent, --min-free-slots, --scale-on-waiting and --scale-in-patience; Terraform the same, as min_free_player_slots and scale_on_waiting_connections. The other terms are set through the API.

Every brake points inward

Scaling out late is a queue. Scaling in early is a disconnection, and only the first is fixed by waiting. So a group:

Scales out before it scales in

And never both in one step.

Waits longer to shrink than to grow

scale_in_patience is longer than scale_out_cooldown by default.

Will not remove a member created moments ago

A member takes a little while to accept its first player.

Will not shrink on numbers it is unsure of

While Plany is still learning a new game's player counts, the group does not shrink at all — an under-reported count would read as an empty server.

A member whose players Plany could not read is never a candidate for removal. scale_in_patience exists because player counts come from your game's own output, which can lag — a server between two rounds reads as empty for a few seconds.

Draining, not disconnecting

Scale-in drains. A draining member keeps the players it has, and is removed once it is empty or past drain_timeout. The deadline is what makes it finish — somebody who never leaves would otherwise hold a member forever — so make it long enough for a match to end inside it. When the group needs to grow again, it brings a draining member back before creating a new one.

For an ephemeral group (ephemeral: true, set through the API) a removed member is deleted; for any other group it is stopped, keeps its world, and is started again when the group grows.

Sleeping members

sleep_after_idle on a group is given to every member: a member with nobody on it for that long goes to sleep, keeps its world and costs nothing to run, exactly as a single server does. A sleeping member still counts toward the group's size, and a player connecting to it — or an allocation — wakes it. It is set through the API, and refused on an ephemeral group.

Rolling out a new build

A group's build is fixed when it is created, so a new version of the game's build does not reach the fleet by itself — two versions in one fleet would match players across them. Change the group's build to move it:

resource "plany_playpute_game_server_group" "prop_hunt" {
group_id = "prop-hunt"
game = "games/garrys-mod"
build = plany_game_build.v2.name
max_size = 8
}

The group keeps creating members on the old build while it warms up a schematic of the new one (pending_build shows which). Once that lands it replaces outdated members within rollout — at most max_surge extra members and max_unavailable missing ones, 25% each unless you set them — and never replaces a member the game has marked allocated: a match finishes on the build it started on. outdated_size counts the members still to go. The build has to be READY.

Allocation

Every Playpute server can use the Agones SDK, and a group can be used the way an Agones fleet is. A game calls Ready() when it can take a match; AllocateGameServer picks a ready server, marks it allocated, writes your labels onto it and returns its addresses — waking it first if it was asleep. Two callers never get the same server, and a server you stopped yourself is never allocated.

grpcurl
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \
-d '{"parent": "projects//locations/",
"labels": {"match": "m-812"}}' \
api.plany.cloud:443 plany.playpute.v1.PlayputeService/AllocateGameServer

selectors narrow the choice by the labels on each member — member_labels on the group sets them. With buffer_size or buffer_percent in the policy, the group keeps that many members ready on top of the allocated ones, and an allocated member is never scaled in. ready_size, allocated_size and sleeping_size on the group report the counts.

Exposure

EXPOSURE_PROJECT_INTERNAL is the right choice for a fleet behind a matchmaker or lobby that dials members from inside the project: members get no public address and nothing a player can find. Choose EXPOSURE_PUBLIC, the default, if players pick a server from a list. In Terraform this is exposure = "PROJECT_INTERNAL".

States

StateMeaning
WARMING_UPCreating and capturing the warm-up server. No members yet.
ACTIVEScaling. The ordinary state at any size, including zero.
PAUSEDScaling suspended by you. Members keep running and keep serving; nothing is created or removed.
FAILEDSomething scaling cannot fix. error says what.
DELETINGBeing removed, members first.

Pause and resume with plany playpute groups update --paused (and --paused=false), or paused in Terraform.

state_message says why the group is where it is — including, when it is holding steady, why it is not scaling. current_size is how many members exist and desired_size how many the group last decided there should be.

Quota

playpute.plany.cloud/game_server_groups is deliberately low: 2 on Hobby, 25 on Pro. A group creates servers without anybody clicking anything, so what this limits is how many independent fleets can be doing that. Members count against the ordinary server quotas — instances, vcpus, memory_bytes. See Quotas.