Team & platformConcept

Warm starts

Starting an interpreter inside a sandbox costs time before the first line of your code runs. Workers pay that cost ahead of time, off the critical path, so the job you start finds a runner already waiting. Nothing to turn on and no paid tier: every script on every plan gets it.

6 min readBefore this: Workers
What you will learn
  • Know what a warm runner is and why each one still serves only one job
  • Tell which JavaScript and TypeScript scripts take the fast path
  • Understand what a flow reserves before a loop starts
  • Tune or turn off the warm pool on a self-hosted worker

A runner already waiting

Each worker keeps a few warm runners for Python and for TypeScript/JavaScript: processes that have already booted the interpreter inside their sandbox and are waiting for a job. When a job is claimed, it is handed one of them, and a replacement starts booting in the background.

ProjectsWorkers3Users
The machines that run your scripts and flows. A worker with no assignment serves every project.
Search workers…Ctrl KOnline · 3Busy · 2live · refreshes every 5s
worker-98142bb7 StatusOnlineSlots3/12Jobs1,284Avg41 msMem212 MBRunning now report.py
worker-606b8c73 StatusOnlineSlots1/12Jobs1,197Avg38 msMem164 MBRunning now greet.ts
worker-eb57a201 StatusOnlineSlots0/12Jobs1,206Avg40 msMem98 MBRunning now—
Every worker on the Workers tab keeps its own warm runners; the average time per job on each card reflects them.

What is kept warm

LanguageHow it starts fast
PythonA pre-booted runner from the warm pool.
TypeScript, JavaScriptA fast path that runs the script in an in-process engine when it can, else a pre-booted runner.
C, C++The compiled binary is cached by its code, so only the first run compiles.
PostgreSQL, MySQLConnections to each database are kept open (up to 4 per connection per worker).

The JavaScript and TypeScript fast path

A script takes the fast path when it bundles cleanly and does not import the waiso SDK. Anything else — a compile error, a thrown error, a timer or file access that needs a full runtime — falls back to the regular runner, whose answer is the one that counts. The fast path serves the latest runtime version only, and code that names the Bun global never takes it.

fast.ts
// Takes the fast path: plain code, no SDK import.
export async function main(n: number = 10) {
  return Array.from({ length: n }, (_, i) => i * i)
}

Flows reserve ahead

A flow knows what is coming. When it is claimed, and again when each loop starts, it asks its worker for enough warm Python runners for the burst — twice the loop’s parallelism, up to a ceiling — so the iterations after the first wave start with no boot at all. Measured on our stack, a loop iteration went from 101 to 45 ms median with no cold starts after the first wave.

A run’s detail shows the difference: a step that had to boot its runner carries a cold start mark with the time it cost. See Performance for every measured figure.

Tune it (self-hosted)

  • WARM_POOL_BASE — warm runners kept per language at rest (2 by default). 0 turns the pool off and every job boots its own runner.
  • WARM_POOL_RESERVE_MAX — the most a flow may reserve on one worker (8 by default, about 110 MB of idle Python). 0 turns reservations off.
  • SQL_POOL_MAX_PER_INTEGRATION — open connections per database connection per worker (4 by default).