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 groupMembers 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.
| Field | Can it change? |
|---|---|
policy, display_name, sleep_after_idle | Yes, at once. |
settings | Yes; members created afterwards get the new settings. |
machine_type, spot | Yes; each member picks the change up the next time it starts. |
build | Yes, as a rolling update. |
game, ephemeral, exposure, vpc | No — 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.
| Field | What it does |
|---|---|
min_size / max_size | The range. max_size is required: it is the ceiling on members, and so on what the fleet can cost. |
target_player_percent | Hold 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_slots | Keep this many player slots free across the group, so a party arriving together finds room without waiting. Adds to the percentage. |
scale_on_waiting_connections | Add a member when somebody is connecting and nobody is playing yet. The only signal that sees demand before it lands. |
target_cpu_percent | Hold the group's average CPU at this percentage of each member's machine type, 1 to 100. |
buffer_size / buffer_percent | Keep this many members ready and unallocated on top of the allocated ones — see Allocation. |
scale_out_cooldown | How long after growing before the group may grow again. |
scale_in_patience | How long a member must have been empty before it may be removed. |
max_scale_out_batch / max_scale_in_batch | Most members to add or remove in one step. |
drain_timeout | How 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:
And never both in one step.
scale_in_patience is longer than scale_out_cooldown by default.
A member takes a little while to accept its first player.
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 -H "authorization: Bearer $PLANY_API_KEY" \ -d '{"parent": "projects//locations/", "labels": {"match": "m-812"}}' \ api.plany.cloud:443 plany.playpute.v1.PlayputeService/AllocateGameServerselectors 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
| State | Meaning |
|---|---|
WARMING_UP | Creating and capturing the warm-up server. No members yet. |
ACTIVE | Scaling. The ordinary state at any size, including zero. |
PAUSED | Scaling suspended by you. Members keep running and keep serving; nothing is created or removed. |
FAILED | Something scaling cannot fix. error says what. |
DELETING | Being 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.