OpenSandbox TTL Expiration: Automatic Sandbox Termination Explained
When an OpenSandbox sandbox exceeds its Time To Live (TTL) expiration, the platform automatically force-terminates the container or pod, transitions the state from Running or Paused to Terminated, and sets the termination reason to ttl_expiry.
OpenSandbox enforces strict resource lifecycle management through configurable Time To Live (TTL) settings that prevent resource leaks in multi-tenant environments. When a sandbox's TTL expiration deadline passes, the system triggers an irreversible automatic cleanup process that ensures resources are reclaimed and the sandbox enters a terminal state. Understanding this behavior is critical for developers building applications that rely on ephemeral execution environments.
What Happens When OpenSandbox TTL Expiration Occurs
When the TTL expiration timer fires, OpenSandbox initiates a forced termination sequence that executes immediately regardless of whether the sandbox is currently processing workloads or in a Paused state.
The Termination State Transition
The lifecycle follows a strict progression defined in the OpenSandbox specification:
- The sandbox transitions from its current active state to
Stopping - The system executes cleanup operations on the container or pod
- The final state becomes
Terminatedwithstatus.reasonexplicitly set to"ttl_expiry"
This transition is atomic and irreversible once the expiration timer triggers. The specs/sandbox-lifecycle.yml file formally defines TTL expiry as a valid termination trigger that mandates the Stopping → Terminated transition.
Timer-Based Cleanup Process
A background timer created during sandbox initialization fires at the exact expires_at timestamp. This timer invokes backend-specific expiration handlers:
- Docker backend: Calls
_expire_sandboxinserver/src/services/docker.py - Kubernetes backend: Executes equivalent logic through the provider's
update_expirationandget_expirationmethods
The cleanup process performs the following actions in sequence:
- Kills the running container or pod if still active
- Removes the container/pod and associated network resources
- Cleans up side-car components such as egress proxies
- Deletes the expiration tracker to prevent post-termination renewal attempts
Implementation Details: Docker and Kubernetes Backends
OpenSandbox supports multiple container backends, each implementing TTL expiration with backend-specific optimizations while maintaining consistent state semantics across the API.
Docker Backend Implementation
In server/src/services/docker.py, the Docker service manages TTL through three key functions that ensure reliable expiration handling:
_schedule_expiration: Registers a backgroundTimerwhen a sandbox launches that fires at theexpires_attimestamp (lines 256-270)_expire_sandbox: Executes the forced termination, killing and removing the container along with its side-car proxy (lines 95-124)_restore_existing_sandboxes: Rebuilds expiration timers when the service restarts, immediately terminating any sandboxes that expired while the service was offline (lines 23-62)
The _expire_sandbox function ensures that even if the container is unresponsive to graceful shutdown signals, it is forcefully removed and the sandbox record updated to Terminated with the ttl_expiry reason.
Kubernetes Backend Handling
The Kubernetes implementation in server/src/services/k8s/kubernetes_service.py relies on the provider's native expiration mechanisms while maintaining API compatibility with the Docker backend. While the renew_expiration endpoint validates future timestamps through ensure_future_expiration in server/src/services/validators.py, the actual enforcement occurs through:
- Provider-specific
update_expirationandget_expirationmethods - Automatic pod termination when the TTL deadline passes
- Transition to
Terminatedstate withttl_expiryreason, consistent with the Docker backend behavior
Code Examples: Working with TTL Expiration
Developers can observe and interact with TTL expiration through the OpenSandbox API to build resilient applications that handle ephemeral execution environments gracefully.
Creating a Sandbox with a 30-Second TTL
The following Python script creates a sandbox with a 30-second TTL and polls until automatic termination occurs:
import requests
import datetime
import time
# Create sandbox with 30-second TTL
payload = {
"image": {"uri": "python:3.11"},
"timeout": 30, # seconds
"metadata": {"example": "ttl-demo"}
}
resp = requests.post(
"http://localhost:8080/v1/sandboxes",
json=payload,
headers={"OPEN-SANDBOX-API-KEY": "my-key"}
)
sandbox = resp.json()
sid = sandbox["id"]
print(f"Sandbox {sid} created, expires at {sandbox['expiresAt']}")
# Poll until TTL expiration triggers termination
while True:
info = requests.get(
f"http://localhost:8080/v1/sandboxes/{sid}",
headers={"OPEN-SANDBOX-API-KEY": "my-key"}
).json()
state = info["status"]["state"]
print(f"[{datetime.datetime.now()}] state={state}")
if state == "Terminated":
print("Sandbox terminated due to TTL expiration.")
break
time.sleep(5)
The timer created in _schedule_expiration fires after approximately 30 seconds, executing _expire_sandbox and moving the sandbox to the Terminated state.
Checking Termination Status via API
After TTL expiration, query the sandbox status to confirm the termination reason:
curl -s -H "OPEN-SANDBOX-API-KEY: my-key" \
http://localhost:8080/v1/sandboxes/<sandbox-id> | jq '.status'
Expected response:
{
"state": "Terminated",
"reason": "ttl_expiry",
"message": "Sandbox terminated because its TTL expired."
}
The reason field is populated by the lifecycle transition defined in specs/sandbox-lifecycle.yml.
Renewing TTL Before Expiration
To prevent automatic termination, extend the TTL before the deadline passes:
import datetime
import requests
# Extend TTL by 5 minutes
new_expiration = (datetime.datetime.utcnow() + datetime.timedelta(minutes=5)).isoformat() + "Z"
renew_resp = requests.post(
f"http://localhost:8080/v1/sandboxes/{sid}/renew-expiration",
json={"expiresAt": new_expiration},
headers={"OPEN-SANDBOX-API-KEY": "my-key"}
)
print("New expiresAt:", renew_resp.json()["expiresAt"])
The ensure_future_expiration validator in server/src/services/validators.py ensures the new timestamp is in the future before updating the timer.
Key Source Files and Functions
Understanding the TTL expiration mechanism requires familiarity with these components:
| File | Function/Component | Purpose |
|---|---|---|
specs/sandbox-lifecycle.yml |
Lifecycle specification | Defines ttl_expiry as valid termination reason and Stopping → Terminated transition |
server/src/services/docker.py |
_schedule_expiration |
Creates background timer at sandbox creation (lines 256-270) |
server/src/services/docker.py |
_expire_sandbox |
Executes forced termination and cleanup (lines 95-124) |
server/src/services/docker.py |
_restore_existing_sandboxes |
Rebuilds timers on service restart, expires overdue sandboxes (lines 23-62) |
server/src/services/k8s/kubernetes_service.py |
Provider expiration methods | Kubernetes-specific TTL enforcement |
server/src/services/validators.py |
ensure_future_expiration |
Validates renewal timestamps are in the future |
server/src/api/lifecycle.py |
renew_expiration endpoint |
HTTP API for TTL extension |
Summary
- Automatic termination: When OpenSandbox TTL expiration occurs, the platform immediately force-terminates the sandbox without manual intervention.
- State transition: The sandbox moves from
RunningorPausedtoStopping, thenTerminatedwithreasonset tottl_expiry. - Timer mechanism: A background timer created at launch triggers
_expire_sandbox(Docker) or equivalent Kubernetes logic at the exact expiration timestamp. - Resource cleanup: The process kills the container/pod, removes network resources, cleans up side-car components like egress proxies, and deletes the expiration tracker.
- Service resilience: On restart,
_restore_existing_sandboxesrebuilds timers and immediately terminates any sandboxes that expired while the service was offline.
Frequently Asked Questions
Can I renew a sandbox's TTL after it has already expired?
No. Once OpenSandbox TTL expiration occurs and the sandbox enters the Terminated state, the expiration tracker is deleted and the sandbox cannot be renewed. The ensure_future_expiration validator in server/src/services/validators.py only accepts future timestamps for active sandboxes, and the _expire_sandbox function removes all tracking data upon termination. You must create a new sandbox with a fresh TTL.
What happens to side-car components like egress proxies when TTL expires?
The _expire_sandbox function in server/src/services/docker.py (lines 95-124) explicitly cleans up side-car components alongside the main container. When the TTL timer fires, the function removes egress proxies and any associated network resources as part of the standard cleanup sequence. This ensures complete resource reclamation and prevents orphaned containers or networking rules from persisting after the sandbox terminates.
Does OpenSandbox handle TTL expiration for sandboxes that were running during a service restart?
Yes. The _restore_existing_sandboxes function in server/src/services/docker.py (lines 23-62) rebuilds all expiration timers when the service restarts. It iterates through existing containers, checks their expires_at timestamps against the current time, and immediately triggers _expire_sandbox for any sandboxes that expired while the service was offline. This mechanism ensures TTL enforcement remains reliable across service restarts and crashes.
How can I detect that a sandbox was terminated due to TTL expiration versus other causes?
Query the sandbox status via GET /v1/sandboxes/{sandboxId}. When TTL expiration triggers termination, the API returns status.state: "Terminated" and status.reason: "ttl_expiry", as defined in specs/sandbox-lifecycle.yml. This distinguishes TTL-driven termination from manual stops (which may use "user_requested") or error conditions (which use specific error codes). The explicit ttl_expiry reason code allows applications to distinguish between expected lifecycle expiration and failure scenarios.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →