Session lifecycle
Templates, live vs paused, hot/warm/cold resume, checkpoints, and the timers that move a session along.
A session is one sandbox you create, use, pause, and destroy. Everything else — templates, checkpoints, fork, timers — is how you start it, save it, or copy it.
template ──► creating ──► live ⇄ paused (hot → warm → cold) ──► destroyed
│ │
checkpoint wake on next op
save as templateStarting — templates
A template is where you start. Official ones (base, python, node, …) or one you published from an earlier session.
inis sessions create --template python
# or pin an exact build:
inis sessions create --template python@2026.07.10The first boot from a template is a cold start — the environment is brought up fresh. After that, if you pause and come back, you are waking the same session (files, packages, and running-process state from when you left), not starting over from the template.
| Start path | What you get |
|---|---|
| Template (official or yours) | New session from a known baseline |
| Checkpoint → new session | New session branched from a save-point of an old one |
Stateless run (inis run, /v1/sessions/exec) | Same create → run → destroy, no ID to keep |
See Concepts for when to keep a session vs run code and throw it away.
Live and paused
| State | Meaning | Compute |
|---|---|---|
creating | Coming up; not ready for exec yet | Brief |
live | Running — exec, files, expose, PTY all work | Yes |
paused | Saved and sleeping — no processes running | No (storage only) |
destroyed / ended | Gone; ended is history metadata only | No |
Live → paused when you pause explicitly, when idle timeout fires, when max_lifetime_ms fires (auto-pause, not destroy — see Timers below), or when the last PTY client leaves (on_pty_detach: pause, the default).
Paused → live on any operation (exec, files, expose, processes) or SSH/PTY attach — wake-on-op. One exception, added deliberately: an expose call that is already satisfied (the port is exposed with exactly the settings asked for) answers from the existing route and leaves the session asleep, so a loop re-asserting the exposure it wants doesn't wake and bill every sleeping session on every pass. You rarely need to call resume yourself. Exception: a session paused by its own max_lifetime_ms firing cannot wake again — resume (explicit or automatic) is refused. That pause is permanent; see max_lifetime_ms in Timers below.
Destroyed when you delete it, when paused_ttl_ms fires, or when a detach policy / completion flag tears it down. A destroyed session's saved snapshot is deleted at the same time. If the session ended because the machine it ran on was lost, or that deletion fails, the snapshot is kept for 30 days after the session ended and then deleted. max_lifetime_ms only destroys as a fallback, if the auto-pause it triggers can't itself succeed (e.g. a wedged VM) — it never destroys a healthy session, it just permanently pauses it.
Resume speed — hot, warm, cold
A paused session is not one speed. The console and API expose snapshot_tier — how ready the session is to wake:
| Tier | Where it sits | Wake feel |
|---|---|---|
| hot | Fast local copy | Instant (tens of ms after a slot is ready) |
| warm | Compressed local copy | Quick (local decompress, typically ~1–2 s) |
| cold | European object storage only | Slower (download + restore, often several seconds) |
The platform demotes dormant sessions over time (and under disk pressure): hot → warm → cold. Any successful wake promotes back toward hot and resets that clock. You do not pick the tier yourself — you see it on the session while it is paused.
Capacity in the console also talks about running (live) vs warm (paused, resumable). Same idea: warm quota is how many paused sessions you can hold.
Saving — pause vs checkpoint vs template
These sound alike. They are different jobs:
| Pause | Checkpoint | Template | |
|---|---|---|---|
| Job | Stop paying compute; come back to this session | Save-point you can roll back to or branch from | Named starting environment for new sessions |
| Outlives the session? | No | Yes (until you delete it) | Yes |
| Typical use | Agent idle between tool calls | Clean baseline / rollback after untrusted code | “Always start from this env” |
# Pause this session (auto-wakes on next op)
inis sessions pause "$SID"
# Save-point on the same session
inis sessions checkpoint "$SID" --name clean
inis sessions restore "$SID" --checkpoint clean
# Publish a reusable start environment (see Templates)
# Console: Save as template — or create new sessions from a checkpointFork is the parallel path: clone a live session into N children for eval / rollouts. Ephemeral — not a long-lived save. Details in Concepts.
Timers
Set at create (Sessions API / CLI flags). Omit for server defaults.
| Knob | Effect |
|---|---|
idle_timeout_ms | Auto-pause after this much idle time while live |
on_pty_detach | pause (default), keep_live, or destroy when the last terminal leaves (PTY only) |
paused_ttl_ms | Auto-destroy after sitting paused this long (0 / omit = default; negative = never) |
max_lifetime_ms | Hard cap on session age (live or paused). Auto-pauses when reached — permanently: unlike every other trigger in this table, this pause can never be resumed from (any attempt refuses with 410); destroy is only a fallback if the pause itself fails. paused_ttl_ms above is what actually bounds how long the paused data sticks around |
destroy_on_completion | Tear down after the first command (stateless-style) |
max_lifetime_ms set together with paused_ttl_ms negative ("never") is rejected at create time (400): a session that max_lifetime_ms permanently pauses and can never resume from would otherwise sit paused and billed forever with no paused_ttl_ms to ever reclaim it. Set paused_ttl_ms to 0 (default) or a positive value whenever you set max_lifetime_ms.
What counts as activity (resets idle): exec, files, expose, process APIs, and an attached PTY. A background process alone does not, unless you start it with keep_alive: true.
inis sessions create --on-detach pause --paused-ttl-ms 86400000 # drop after a day paused
inis sessions create --on-detach keep_live # stay live after detach; idle still applies laterLimits
Defaults and hard caps on the managed cloud today:
| Knob | Default | Hard cap |
|---|---|---|
idle_timeout_ms | Off (no auto-pause) | 1 hour |
max_lifetime_ms | Off (no cap of your own) | 24 hours |
| Continuous-live backstop | Always on — 24 hours continuously live (6 hours on a new, unverified trial account), independent of max_lifetime_ms. Any pause/resume resets the clock | 24 hours (6 on trial) |
paused_ttl_ms | 30 days | — (negative disables it) |
timeout_ms (one exec) | 30 seconds | 1 hour |
| Concurrent live sessions per account | 10 (1 on a new, unverified trial account) | — |
| Cumulative disk across live + paused sessions | 50 GB (5 GB on trial) | — |
A session lives until you destroy it, it idles out, or its own opt-in max_lifetime_ms fires — there is no default cap on total age.
The continuous-live backstop never destroys — and it doesn't leave you down, either. When it fires it force-pauses (closing any attached PTY, tunnel, or in-flight operation), takes a fresh snapshot, and — normally — immediately resumes the session again: a few-second "blip," not a daily outage. That resume-from-fresh-snapshot cycle doubles as a daily durability checkpoint for a session that would otherwise never produce one. A session.active_window_warning event fires a few minutes ahead of the blip on the account event stream so you can quiesce in-guest work first — see Execution log.
The one exception: if the backstop fires while your session's host node happens to be draining for a fleet cycle, it skips the immediate resume and leaves the session parked instead — the same behavior as before this cycling existed. That's rare and self-resolving: any later request resumes the session normally, same as any other paused session.
No requests-per-second limit applies to the execution API itself — the concurrency cap is what bounds usage. Sign-in and CLI-pairing endpoints are separately rate-limited per IP/email as a brute-force guard.
Keeping a session live
To avoid idle pause while work continues without API traffic:
- Keep a PTY attached (
inis ssh) - Start a named process with
keep_alive: true - Use
on_pty_detach: keep_liveso disconnecting does not pause immediately
Without keep_alive, a detached process can keep running until idle pause freezes the whole VM.
Billing (short)
- Live — compute, metered by the millisecond of run time; starting and stopping are not billed
- Paused — storage for the saved session (tier may change; you still pay while it exists)
- Destroyed — nothing ongoing
Prefer pause over destroy when you will return: you keep the environment and avoid another template cold start.
Recipes
Agent — pause between model turns
session = client.sessions.create(template="python", idle_timeout_ms=60_000)
session.exec(["pip", "install", "httpx", "-q"])
# ... tool calls ...
session.pause() # optional; idle timeout would do this anyway
session.exec(["python", "agent_step.py"]) # wakes automaticallyStart from a template, save a baseline
SID=$(inis sessions create --template data-science)
# ... install project deps, seed data ...
inis sessions checkpoint "$SID" --name ready
# later: roll back in place, or stamp new sessions from the checkpointPreview server that should stay up
session.start_process("web", ["python3", "-m", "http.server", "8080"], keep_alive=True)
session.expose(8080)If the session later pauses, preview clients disconnect; after wake they reconnect to the same URL.
If a node fails
Sessions run on dedicated hardware, and hardware occasionally dies. If a
session's host is unexpectedly lost, the session moves to a terminal
node_lost state — it does not silently retry, and inis.run does not
auto-restore it onto another node. Every operation against it (exec, files,
pty, pause, fork, checkpoint, resume) returns the session_node_lost
error, with two exceptions: GET /v1/sessions/{id} keeps working and
reports state: "node_lost", so you can detect it instead of guessing from
a timeout, and DELETE /v1/sessions/{id} still works too — deleting a lost
session always succeeds, so it never lingers, undeletable, in your session
list.
Recreate the session from its template, or restore from your last checkpoint if you took one — anything since that checkpoint is gone. See Errors for the full code reference and Production best practices for a handler that branches on it.
Gotchas
- Tier is informational — you cannot force hot; long idle or disk pressure can move a session to cold before the next wake.
- Cold wake is slower — treat it as normal for abandoned sessions, not an error.
on_pty_detachis PTY-only — API-only sessions use idle timeout / explicit pause.- Previews drop on pause — preview connections drop while the session is paused; reconnect after wake.
- Secrets in paused / checkpointed state — values already in guest memory can be retained with the saved session. See Sessions API.
- Wake races — a live-only call that hits a just-paused session may return
session_not_live(409); refetch and retry. See Errors.
Where to go next
- Templates — official set, versioning, pinning
- Best practices — persistence, resume/fork, templates, scoped credentials, egress
- Concepts — fork, checkpoints, preview URLs
- Sessions API — request/response reference
- CLI —
inis sessions,inis ssh - Networking — egress across pause / fork
- Production best practices — which timer to reach for, why you don't need your own warm pool, reconnecting after resume