PlanyDocs
Playpute

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

SKUUnitOn demandSpot
playpute-vcpu-hour / playpute-spot-vcpu-hourvCPU-hour$0.016$0.0064
playpute-gib-hour / playpute-spot-gib-hourGiB-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 spot

Changing 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-action

It answers 404 until a notice exists, and then 200 with:

JSON
{ "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-action
while 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 5
done

2. 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:00Z

The 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.Once
err = 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_disk is 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_fallbackA start with no spot capacity
trueStarts 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:

FieldMeaning
running_on_spotThe current run is on spot capacity. False for a server that fell back, and for one that is not running.
termination_timeSet once a notice has been given: when the current run will be stopped.
last_reclaim_timeWhen this server was last reclaimed.
preserving_diskA reclaimed server's world is still being kept; a start is refused until it finishes.
noticeHow 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 types

ListMachineTypes gives, per type:

FieldMeaning
spot.available_nowA 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_7dFraction of the last seven days in which one could have been placed, 0 to 1.
spot.reclaims_per_day_7dReclaims per server-day over the last seven days. 0.1 is one reclaim every ten days of running.
spot.sample_timeWhen 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.