Expire pools and claims automatically
Set a creation-age TTL so sandbox pools and claims delete themselves.
Pools and claims accept an optional creation-age TTL. Pass
ttl_seconds_after_created= (requires cua-sandbox>=0.4.2) and the resource
is deleted once it has existed that many seconds, whether or not it is in use.
Without this argument, there is no creation-age TTL from this setting. Explicit
claim lifecycle deadlines, normal claim release, pool deletion, and platform
policies can still end resources.
This guide assumes a pool created as in Create Fleet capacity.
Expire a pool#
Pass the TTL to Pool.apply() for a pool you own to request automatic deletion
a fixed time after the pool was first created:
from cua_sandbox import Image, Pool
# Delete the whole pool 24 hours after it was first created.
pool = await Pool.apply(
Image.from_registry(IMAGE),
name=POOL_NAME,
replicas=1,
ttl_seconds_after_created=86400,
)Expire a claim#
Pass the same argument to pool.claim() (or pool.create_claim()) to delete
one claim and its sandbox a fixed time after the claim was created:
# Delete this claim and its sandbox one hour after the claim was created.
async with pool.claim(
name=CLAIM_NAME,
ttl_seconds_after_created=3600,
) as sandbox:
...A claim TTL fills in the claim's lifecycle shutdown time with a Delete
policy; a claim that already carries an explicit lifecycle keeps it. Exiting
the claim context still releases it immediately, even if its TTL has not
elapsed. Disconnecting alone does not release it or cancel its deadline.
The clock starts at creation#
The TTL counts from the resource's original creation, not its last use:
re-running Pool.apply() reconciles the existing pool without resetting its
age, and reconnecting to a named claim keeps the deadline set when the claim
was first created. Passing a TTL when attaching to an existing named claim
does not replace that claim's spec or renew its deadline. A pool may therefore
expire shortly after a rerun if it is already near the end of its lifetime.
Use a TTL as a cleanup backstop for interrupted jobs, and still release claims when work finishes. It is not an inactivity timeout or an automatic heartbeat. Keep the pool lifetime longer than the intended work, including startup time, and copy results out before the deadline. Controller-driven deletion is asynchronous; verify resource absence after expiry instead of treating the deadline as proof that cleanup has finished.
Hand-built claim specs#
When you build a claim spec by hand, put the TTL inside it — passing both
spec= and ttl_seconds_after_created= raises ValueError:
from cua_sandbox import ClaimSpec
spec = ClaimSpec(
sandbox_template_ref=pool.resource.spec.sandbox_template_ref,
warmpool=None,
bind_deadline=None,
lifecycle=None,
ttl_seconds_after_created=3600,
)
async with pool.claim(spec=spec) as sandbox:
...Pool expiry under live claims#
Treat a pool TTL that can expire while claims are held as a capacity event, not just cleanup: deleting the pool drains its sandboxes, and a claim that outlives its pool destroys its sandbox on release instead of returning it to the pool.
If cleanup was interrupted, use the resource identities you recorded and inspect the remaining pool and claims. Reapplying a pool is a configuration write, not a cleanup operation.