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:

  1. The sandbox transitions from its current active state to Stopping
  2. The system executes cleanup operations on the container or pod
  3. The final state becomes Terminated with status.reason explicitly 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_sandbox in server/src/services/docker.py
  • Kubernetes backend: Executes equivalent logic through the provider's update_expiration and get_expiration methods

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 background Timer when a sandbox launches that fires at the expires_at timestamp (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_expiration and get_expiration methods
  • Automatic pod termination when the TTL deadline passes
  • Transition to Terminated state with ttl_expiry reason, 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 Running or Paused to Stopping, then Terminated with reason set to ttl_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_sandboxes rebuilds 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →