Concepts
Lifecycle & wake-on-join

Lifecycle & wake-on-join

TrueTick servers are scale-to-zero. A server with no players hibernates once its idle timeout runs out — it stops consuming CPU/RAM and stops billing — and comes back online the moment someone needs it. This is the mechanism behind metered billing: you pay for runtime, the idle wait before hibernation included, and nothing while the server is hibernated.

States

A server moves through a small set of states, reported in state:

StateMeaning
stopped / hibernatedScale-to-zero. Not running, not billing. Data preserved
startingContainer booting / world loading
runningLive and serving players. Billing accrues (metered plan)
stoppingGraceful shutdown in progress (world saved)
installing / install_failedModpack pre-install in progress / failed (see Mods)

You drive transitions with start, stop, and restart. Always treat state as the source of truth and poll it after a transition rather than assuming instant completion.

Wake-on-join

A hibernated server has a lightweight proxy listening on its Minecraft port. When a player connects to my-smp.truetick.gg:25565, the proxy:

  1. Parses the connection handshake.
  2. Boots the real server container.
  3. Hands the player through once it's ready.

What the player sees while that happens depends on their client:

  • Minecraft 1.20.5+ (supports the Transfer packet), Paper-family or vanilla backend: the player is taken into a lightweight in-game holding session instead of being left on the login screen. It shows a title ("<address> is waking up") and an action-bar counter — real elapsed seconds, ticking up, never a fake percentage — then transfers them to the real server the moment it's ready. This holding session has its own time bound (3 minutes in the shipped config); past it, the player is disconnected with the same "still waking up, rejoin shortly" message as the older-client path below.
  • Older clients, or when the holding session isn't available (modded handshake, per-IP cap, feature disabled): the player is held on the ordinary login screen for up to 20 seconds. If the server isn't ready by then, they're disconnected with a message asking them to rejoin in a moment — the wake keeps going in the background, so the next join usually lands on an already-running server.

Either way, no API call is needed — this is automatic. The sleeping proxy also answers the server-list ping with a MOTD and a sleep line, so the server still looks alive in the multiplayer list.

Programmatic wake. If you want to bring a server up without a player joining (e.g. before a scheduled event, or in a CI job), call POST /v1/servers/{id}:start. Same boot path, triggered by you instead of a join.

Cold start

Boot time depends on the core and content. We don't quote a typical figure until production measures one:

  • Vanilla / Paper / Purpur: no fixed number here — world size and first-load chunk generation both move it.
  • Modded (Forge/NeoForge/Fabric with mods): longer — large modpacks take minutes on first boot, and TrueTick allows up to a 10-minute start window for modded servers.

Either way, poll state rather than assuming a fixed duration: the readiness check confirms the server is actually accepting players before marking it running.

Idle hibernation

When a server has had no players for idleTimeoutMinutes, the idle watcher hibernates it back to scale-to-zero. Set the timeout per server via setProperties(id, { idleTimeoutMinutes }). A shorter timeout saves money on bursty workloads; a longer one avoids re-waking during brief lulls.

0 (the default for new servers) means the platform default — currently 8 minutes. There is no "never hibernate" value on metered plans; a server that should run 24/7 belongs on a flat plan, which is exempt from idle hibernation entirely.

Putting it together

A typical metered lifecycle:

create (stopped, $0)
   → player joins  ──► wake-on-join ──► running (billing)
   → empty for idleTimeoutMinutes ──► hibernate (stopped, $0)
   → player joins again ──► wake ──► running …

For an agent or CI workflow that controls timing explicitly:

await client.servers.start("ci-test");        // cold start — poll for state === "running"
// … poll until state === "running", run your tests …
await client.servers.stop("ci-test");          // stop billing immediately

See Billing for how runtime maps to cost, and the Ephemeral test server guide for the CI pattern end to end.