Spot
Run game servers on spare capacity at 60% off, with two minutes' notice before the capacity is taken back.
Plany keeps capacity on hand so an ordinary server starts in seconds, and much of the time some of it is idle. Spot is that idle capacity, sold at 60% off Playpute's vCPU and memory rates.
The price of the discount is that it can be taken back. When an on-demand server needs the room, a spot server on it is given two minutes' notice and then stopped. Everything on its disk is kept.
Spot suits a server that is worth running cheaply and can be interrupted: a minigame between rounds, a creative world, a fleet member the group will replace. It suits a raid in its final minute less well.
Spot is on the Pro plan only. Turning it on for an account on any other plan
is refused, with a link to upgrade. A server whose account has since left Pro
keeps its spot configuration and starts on demand, at the on-demand rate, and
its state_message says so.
What it costs
| SKU | Unit | On demand | Spot |
|---|---|---|---|
playpute-vcpu-hour / playpute-spot-vcpu-hour | vCPU-hour | $0.016 | $0.0064 |
playpute-gib-hour / playpute-spot-gib-hour | GiB-hour | $0.007 | $0.0028 |
Disk is charged at the ordinary rate — a spot server's world costs the same to
keep as anybody else's. Per machine type, a standard-4 is $0.120 an hour on
demand and $0.048 on spot; the full table is on Machine
types.
Billing follows where the server actually runs. A spot server that fell back to on-demand capacity is billed at the on-demand rate for that run.
Turning it on
Servers → New server → choose the game and a machine type → Spot → Run on spotChanging spot takes effect on the next start, the same as changing
machine_type. A running server is not moved on or off spot under its players.
The two-minute notice
When the capacity is needed back, the server is told first. The notice reaches
the game three ways, and a game may use any of them — or none, in which case it
is simply stopped at the deadline the way plany playpute servers stop would
stop it, with its ordinary stop signal and stop_timeout.
Whatever you do with the notice, save. Two minutes is enough to write a world, flush a match result and tell players what is happening; it is not enough to wait for the round to finish.
1. The notice endpoint
From inside the server, ask:
GET http://169.254.169.254/metadata/spot/instance-actionIt answers 404 until a notice exists, and then 200 with:
{ "action": "stop", "time": "2026-09-30T14:02:00Z" }time is when the server will be stopped, in RFC 3339; action is always
stop. The address answers only from inside the server itself, and needs no
credential. Poll every five seconds or so; anything more frequent buys nothing.
#!/bin/sh# Runs beside the game; tells it to save when a notice arrives.URL=http://169.254.169.254/metadata/spot/instance-actionwhile true; do body=$(curl -fsS "$URL" 2>/dev/null) && { echo "spot notice: $body" rcon-cli "say Server is being moved in two minutes - saving." rcon-cli "save-all flush" exit 0 } sleep 5done2. The Agones SDK
A game built against the Agones SDK receives the notice as an annotation on its
own GameServer, delivered by WatchGameServer:
plany.cloud/spot-termination-time: 2026-09-30T14:02:00ZThe same RFC 3339 time as the endpoint. The Agones SDK is available on every Playpute server, not only on Agones Autopilot.
s, err := sdk.NewSDK()if err != nil { log.Fatal(err)} var once sync.Onceerr = s.WatchGameServer(func(gs *coresdk.GameServer) { deadline, ok := gs.GetObjectMeta().GetAnnotations()["plany.cloud/spot-termination-time"] if !ok { return } once.Do(func() { log.Printf("spot notice: stopping at %s", deadline) go saveAndDrain() })})3. A process signal
Name a signal in notice_signal and the game's process is sent it when the
notice arrives — --spot-notice-signal SIGUSR1 in the CLI, notice_signal in
the Terraform spot block.
Empty sends nothing, and that is the default for a reason. The default action
of most signals is to terminate the process, so naming one the game does not
handle stops it two minutes early and skips the save you wanted. Name a signal
only when the game handles it. SIGTERM and SIGKILL are refused — the stop
that follows the notice sends the game's own stop signal already.
What a reclaim keeps
Everything on disk. A reclaimed server is stopped at the deadline with its
ordinary stop signal, shows STOPPED with a state_message saying spot capacity
was reclaimed, and keeps its world for its next start, wherever that runs.
It is not restarted for you — start it again, or let a player connecting to its address start it. Three things to know about that start:
- It is slower than an ordinary start, because the world is moved to where the server runs next. Later starts are ordinary again.
- A start while the world is still being kept is refused as retryable
(
UNAVAILABLE), because starting earlier would boot the server without its world.spot_status.preserving_diskis true for exactly that window; the console shows it as Preserving world. - What was in memory is not kept. A reclaimed server starts fresh from its disk — the world loads, the game initialises — rather than resuming in seconds the way an ordinary stop does.
An ephemeral server keeps nothing by definition, so a reclaim is simply a stop and its next start is fresh.
When there is no spare capacity
Spare capacity is whatever on-demand servers are not using, so sometimes there is none of the type you asked for.
on_demand_fallback | A start with no spot capacity |
|---|---|
true | Starts on demand, at the on-demand rate. |
false (default) | Waits for spot capacity for about a minute, then fails with a no-capacity error. |
Fallback is right for a server players are waiting on; no fallback is right for
one that is only worth running cheaply. A server that fell back stays on demand
until it next stops — it is not moved to spot mid-session when capacity frees
up. spot_status.running_on_spot says which it is right now.
Spot status
Every server carries spot_status, output only:
| Field | Meaning |
|---|---|
running_on_spot | The current run is on spot capacity. False for a server that fell back, and for one that is not running. |
termination_time | Set once a notice has been given: when the current run will be stopped. |
last_reclaim_time | When this server was last reclaimed. |
preserving_disk | A reclaimed server's world is still being kept; a start is refused until it finishes. |
notice | How long the notice was, for drawing a countdown. |
plany playpute servers get shows it, the console's server page counts down to
termination_time, and the Terraform resource exposes it as the computed
spot_status.
Groups
machine_type and spot on a group apply to every
member. A fleet is where spot is most useful — a minigame server reclaimed
between rounds costs nobody anything. A reclaimed member of an ephemeral group is
replaced with a new one; in any other group it is started again with its world.
With on_demand_fallback, the replacement runs on demand when spot has nothing
free, which keeps a fleet at size while the region is busy.
How likely is a reclaim
Each machine type carries its spot record in your region. You are told whether you could have spot capacity of a type, and how often it was taken back.
Machine typesListMachineTypes gives, per type:
| Field | Meaning |
|---|---|
spot.available_now | A spot server of this type could be placed right now. A reading, not a promise: the next on-demand server may take it. |
spot.availability_7d | Fraction of the last seven days in which one could have been placed, 0 to 1. |
spot.reclaims_per_day_7d | Reclaims per server-day over the last seven days. 0.1 is one reclaim every ten days of running. |
spot.sample_time | When the record was last sampled. Unset means never — not zero availability. |
QuerySpotHistory returns hourly points over at most thirty days, oldest
first; with no interval it reads the last seven, and an interval reaching
further back than thirty days is refused, because nothing older is kept. Each
point has availability, reclaims, running_hours and measured. A point
with measured: false is a gap, not zero availability — draw nothing there
rather than a drop. plany playpute machine-types spot-history --daily rolls
them up by day.
Terraform's plany_playpute_machine_types data source exposes each type as
id, name, display_name, family, vcpus, memory_bytes,
spot_available_now, spot_availability_7d, spot_reclaims_per_day_7d and
spot_sample_time; plany_playpute_machine_type reads one by id.