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:
| State | Meaning |
|---|---|
stopped / hibernated | Scale-to-zero. Not running, not billing. Data preserved |
starting | Container booting / world loading |
running | Live and serving players. Billing accrues (metered plan) |
stopping | Graceful shutdown in progress (world saved) |
installing / install_failed | Modpack 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:
- Parses the connection handshake.
- Boots the real server container.
- 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 immediatelySee Billing for how runtime maps to cost, and the Ephemeral test server guide for the CI pattern end to end.