Skip to main content
Template warm pools keep tenant-owned ready capacity for a template. Use them when you expect a burst of sandboxes from the same prepared environment and the template start_cmd plus readiness probes already produce the state each sandbox should start from. Warm pools do not preserve per-run mutable state. A checkout claims one ready VM and turns it into a normal sandbox; it is not recycled back into the pool after use. Keeping a pool warm reserves runtime capacity. Treat min_ready, max_ready, CPU, and memory as cost-bearing choices, and drain or delete pools when the burst ends.

Create A Pool

Build or identify a template first, then create a pool with public readiness targets:
from nullspace import Template, TemplateWarmPool, wait_for_port

builder = (
    Template.from_python_image("3.12")
    .pip_install(["fastapi", "uvicorn"])
    .copy("./app.py", "/workspace/app.py")
    .set_workdir("/workspace")
    .set_start_cmd(
        "uvicorn app:app --host 0.0.0.0 --port 8080",
        readiness=wait_for_port(8080),
    )
)

build = Template.build(builder, name="review-api", tags=["stable"])

pool = TemplateWarmPool.create(
    build.canonical_ref,
    name="review-api-small",
    min_ready=2,
    max_ready=4,
    vcpus=2,
    memory_mb=1024,
    version_policy="pinned_build",
)
pool.wait_until_ready(min_ready=2)
print(pool.id)
If you already have a template object, Template.create_warm_pool(...) is a convenience wrapper around TemplateWarmPool.create(...). The equivalent CLI flow uses --json for automation:
nullspace template warm-pool create team/review-api:stable \
  --name review-api-small \
  --min-ready 2 \
  --max-ready 4 \
  --vcpus 2 \
  --memory 1024 \
  --json

nullspace template warm-pool wait twp_review_api_small --min-ready 2 --json
The HTTP API surface is /v1/template-warm-pools with create, list, get, update, delete, status, enable, disable, drain, and reconcile operations.

Create From A Pool

Pass the explicit pool ID when creating sandboxes:
from nullspace import Sandbox

sandbox = Sandbox.create(
    template=build.canonical_ref,
    warm_pool=pool.id,
    warm_pool_mode="prefer",
    warm_pool_wait_ms=1500,
)
print(sandbox.get_info().warm_pool_checkout)
warm_pool_checkout is present when create considered a warm pool. It reports source, hit, id, and mode so callers can distinguish a ready checkout from a cold fallback or explicit bypass.

Checkout Modes

ModeBehavior
preferWait up to warm_pool_wait_ms for ready inventory, then fall back to cold create on a miss.
requireWait up to warm_pool_wait_ms, then fail with warm_pool_unavailable instead of cold creating.
bypassForce cold create. This may omit warm_pool when the caller wants to skip pools for one request.
Use prefer for general latency improvement and require when the work should only run if ready capacity is available. In the CLI, pass --warm-pool-mode require for the latter behavior.

Poolable Shape

Template start_cmd and readiness are the pool startup contract. The command runs during template build before snapshotting, and warm pools restore that ready template state. create-time envs, volume mounts, cwd overrides, desktop settings, custom networking, non-destroy timeout policy, and auto-resume are not poolable startup state. In prefer mode, unsupported create-time shape falls back to a cold create. In require mode, it fails with warm_pool_unavailable. snapshot restore, resume, fork, hibernate, and pause do not use template warm-pool checkout. Use reusable snapshots or fork when you need mutable state captured from a running sandbox, hibernate or auto-resume when one sandbox should pause and continue later, and volumes when data must persist across sandbox cleanup.

Version Policy

pinned_build pools resolve the template ref to one build and continue filling that build until updated. track_tag pools follow a template tag. When the tag moves, the pool records a new rollout generation, stops filling old ready or warming inventory, drains old ready rows, and fills the new build. Sandboxes already claimed from the old build continue as normal user sandboxes.
nullspace template warm-pool create team/review-api:stable \
  --name review-api-stable \
  --min-ready 2 \
  --max-ready 4 \
  --version-policy track_tag \
  --track-tag stable \
  --json

Status And Troubleshooting

Inspect status before a burst:
pool = TemplateWarmPool.get("twp_review_api_small")
info = pool.get_info()
print(info.status.ready, info.status.warming, info.degraded_reason)
Status counts are target, ready, warming, claimed, draining, failed, expired, and unavailable. Common degraded reasons:
ReasonMeaning
no_eligible_hostsNo compatible runtime host can fill the pool.
artifact_not_readyThe resolved template artifact is not ready on an eligible host yet.
artifact_prewarm_failedPreparing the template artifact for pool fill failed.
quota_exceededWarm capacity is blocked by an account or deployment quota.
template_build_unavailableThe resolved template build or artifact is no longer usable.
controller_disabledPool reconciliation is disabled by the deployment.
drain_activeThe pool is draining and will not refill ready capacity.
If a burst must not cold start, use require and handle warm_pool_unavailable explicitly. If cold fallback is acceptable, use prefer and inspect warm_pool_checkout.source after create.

Drain And Delete

Drain a pool when the burst ends or before replacing it:
nullspace template warm-pool drain twp_review_api_small --json
nullspace template warm-pool delete twp_review_api_small --json
Draining stops refill and releases ready inventory over the normal controller budget. Deleting a pool requests drain; it does not destroy sandboxes that were already checked out.