Agent Sandbox on Kubernetes: Templates, Warm Pools, Claims, and Lifecycle
Last updated: July 19, 2026
Agent Sandbox
A Pod runs containers. A Sandbox represents one environment assigned to one piece of work.
That environment might belong to an AI agent running a Python script, a user opening a development workspace, or a service processing an isolated task. It may need to stay alive across several interactions, keep files, expose a stable endpoint, start quickly, and disappear when the work is done.
Agent Sandbox adds that lifecycle around a Kubernetes Pod without tying the API to a particular runtime or cluster provider.
The API is built from four resources. Read them as one short workflow:
1SandboxTemplate describes the environment
2SandboxWarmPool keeps clean environments ready
3SandboxClaim requests one environment
4Sandbox owns the environment and its PodThe rest of this post shows how those pieces work together and what changes in real scenarios such as networking, persistent storage, and warm or cold allocation.
Why Not Just Create a Pod?
A Pod is often enough for a normal application or batch job. But a raw Pod does not answer several questions that appear with agent workloads:
- Which environment belongs to this agent run?
- Should it be running, suspended, finished, or expired?
- How can another process claim a ready environment without selecting Pods itself?
- Can files survive if the underlying Pod is recreated?
- How do we keep clean environments ready for the next request?
- When should the environment and its resources be removed?
A Sandbox provides a stable object for that lifecycle. The Pod still runs the containers, while the Sandbox controller creates it, observes it, and cleans it up.
1Pod: Create → Schedule → Run → Terminate
2
3Sandbox: Prepare → Allocate → Use → Finish or Expire → Clean up
4 └─ Pod execution happens hereA Deployment is still better for interchangeable serving replicas, and a Job is still better for ordinary finite batch work. A Sandbox fits when the unit we care about is one environment for one user, agent, or task.
The Four Resources
Sandbox
Sandbox is the core resource. One Sandbox manages one Pod-based environment and can optionally manage persistent volumes and a headless Service.
You can create one directly when pre-warmed capacity is not needed. This first snippet shows only the minimum resource shape:
1apiVersion: agents.x-k8s.io/v1beta1
2kind: Sandbox
3metadata:
4 name: python-script-runner
5spec:
6 podTemplate:
7 spec:
8 containers:
9 - name: python
10 image: python:3.13-alpineThe image’s default process controls how long this minimal Sandbox runs. The complete example later adds a placeholder long-running process so the agent can execute several commands; a production image would normally start its own runtime or execution endpoint.
SandboxTemplate
SandboxTemplate is the reusable blueprint for Sandboxes created through a pool. It holds the common Pod configuration, optional storage and Service settings, network rules, and policies controlling what a claim may customize.
Templates are normally owned by the platform team so applications do not repeat a large Pod definition with every request.
1apiVersion: extensions.agents.x-k8s.io/v1beta1
2kind: SandboxTemplate
3metadata:
4 name: python-agent-template
5spec:
6 podTemplate:
7 spec:
8 containers:
9 - name: python
10 image: python:3.13-alpineSandboxWarmPool
SandboxWarmPool maintains a target number of ready, clean, and unclaimed Sandboxes from a template.
replicas controls the pool’s ready capacity—not the replica count of an application. The replenishment behavior is illustrated in How Allocation Works.
1apiVersion: extensions.agents.x-k8s.io/v1beta1
2kind: SandboxWarmPool
3metadata:
4 name: python-agent-pool
5spec:
6 replicas: 3
7 sandboxTemplateRef:
8 name: python-agent-templateSandboxClaim
SandboxClaim is how an application asks for one environment from a warm pool. The caller does not choose a Pod or race with other callers. It creates a claim, waits until the controller assigns a Sandbox, and reads the assigned name from claim status.
A claim usually maps naturally to one agent run, user workspace, or isolated task.
1apiVersion: extensions.agents.x-k8s.io/v1beta1
2kind: SandboxClaim
3metadata:
4 name: customer-report-run
5spec:
6 warmPoolRef:
7 name: python-agent-poolHow Allocation Works
The platform first creates a template and a warm pool. The pool controller creates Sandboxes from the template until its desired ready capacity is available.
1Before a claim
2
3Warm Pool: [Ready A] [Ready B] [Ready C]When a claim arrives, one ready Sandbox is assigned to it. The pool then creates a clean replacement for the next request.
1While replenishing
2
3Agent Run: [Ready A — claimed]
4Warm Pool: [Ready B] [Ready C] [Starting D]Once the replacement becomes ready, spare capacity is restored:
1Agent Run: [Ready A — claimed]
2Warm Pool: [Ready B] [Ready C] [Ready D]Ready D is not a replica of the current agent run. It is unused capacity for a future claim. A used Sandbox is not placed back into the pool for another user; the pool replenishes with a clean one.
This is the main difference from a normal replica controller: a warm pool manages available inventory, not serving replicas.
A Python Agent Workflow, End to End
Suppose a user asks an agent to analyze a set of values and return a small report. The agent can reason about the request in its own service, but the Python it generates should run in a separate environment. We want that environment ready quickly, isolated from other runs, and automatically cleaned up.
1. Define the Python Environment
1apiVersion: extensions.agents.x-k8s.io/v1beta1
2kind: SandboxTemplate
3metadata:
4 name: python-agent-template
5spec:
6 podTemplate:
7 metadata:
8 labels:
9 app: python-agent-sandbox
10 spec:
11 automountServiceAccountToken: false
12 securityContext:
13 runAsNonRoot: true
14 runAsUser: 1000
15 containers:
16 - name: python
17 image: python:3.13-alpine
18 command: ['sleep', 'infinity']
19 securityContext:
20 allowPrivilegeEscalation: false
21 capabilities:
22 drop: ['ALL']
23 resources:
24 requests:
25 cpu: 100m
26 memory: 128Mi
27 limits:
28 cpu: '1'
29 memory: 512Mi
30 networkPolicyManagement: Managed
31 networkPolicy:
32 ingress: []
33 egress: []
34 envVarsInjectionPolicy: DisallowedThe warm pool keeps ready Sandboxes, each backed by a running Pod. Because the generic Python image has no long-running workload, its container would exit, the Pod would stop being Ready, and the Sandbox could not count as ready pool capacity. This example therefore uses sleep infinity; omit it when the production image already starts an agent runtime or execution service.
2. Keep Ready Capacity
The warm pool is a separate resource that points to the template:
1apiVersion: extensions.agents.x-k8s.io/v1beta1
2kind: SandboxWarmPool
3metadata:
4 name: python-agent-pool
5spec:
6 replicas: 2
7 sandboxTemplateRef:
8 name: python-agent-template
9 updateStrategy:
10 type: OnReplenishThis pool keeps two ready Python environments available. Empty ingress and egress lists in the template apply a default-deny policy in both directions.
| Update strategy | What happens after the template changes | Best fit |
|---|---|---|
OnReplenish | Existing members stay; replacements use the new template as capacity is replenished | Gradual updates without unnecessary churn |
Recreate | Stale, unclaimed members are removed and rebuilt | Faster convergence of all available capacity |
3. The Agent Requests an Environment
When the user request arrives, the agent service creates a claim rather than creating or selecting a Pod itself:
1apiVersion: extensions.agents.x-k8s.io/v1beta1
2kind: SandboxClaim
3metadata:
4 name: customer-report-run
5spec:
6 warmPoolRef:
7 name: python-agent-pool
8 additionalPodMetadata:
9 labels:
10 workload: customer-report
11 annotations:
12 example.dev/request-id: request-123
13 lifecycle:
14 shutdownPolicy: DeleteForegroundLabels and annotations can identify the request for policy, cost reporting, or observability without changing the environment itself.
The agent waits for the claim to receive a Sandbox, then reads the assigned name from status:
1kubectl get sandboxclaim customer-report-run -w
2
3SANDBOX_NAME=$(kubectl get sandboxclaim customer-report-run \
4 -o jsonpath='{.status.sandbox.name}')For this request, a ready pool member can be adopted unchanged, so allocation follows the warm path. Other requests may need a cold start:
| Request | Path | Reason |
|---|---|---|
| Uses the template as-is | Warm | A matching Sandbox is already running |
| Adds only labels or annotations | Warm | Metadata can be applied during allocation |
| Injects environment variables | Cold | The container needs different configuration |
| Adds or overrides volume templates | Cold | Storage must be provisioned before startup |
| Arrives when the pool has no ready capacity | Cold | A new Sandbox and Pod must be created |
The official documentation describes warm allocation as happening in milliseconds. This means assigning an already-running Sandbox; it is not a universal end-to-end guarantee. Track agent_sandbox_claim_startup_latency_ms at p50, p95, and p99, and measure connection plus first-command latency separately. Cold-start time also includes scheduling, image availability, storage, admission, networking, and runtime startup.
Keep common images, dependencies, environment, and storage in the template, and size readyReplicas for expected bursts. Claim-level environment variables and volumes are valuable when customization matters more than latency, but they should not sit on the fast path by accident.
The template’s envVarsInjectionPolicy controls whether a claim may add environment variables: Disallowed rejects them, Allowed permits new names, and Overrides also permits replacing template values. A claim can select a container with containerName; otherwise the first template container is used.
4. The Agent Executes Code for the User Prompt
After allocation, the agent turns the user prompt into a small Python program. An authorized agent service sends that program over the Kubernetes API and executes it inside the assigned environment:
1kubectl exec -i "sandbox/${SANDBOX_NAME}" -c python -- \
2 python - > result.txt <<'PY'
3values = [18, 24, 31]
4print({"total": sum(values), "count": len(values)})
5PYIn a real application, a Kubernetes client library would normally perform the same exec operation, capture standard output and errors, and return the result to the user. The shell example makes that boundary visible: the agent orchestrates the task, while generated Python runs inside the Sandbox instead of inside the agent service.
5. The Agent Releases the Environment
When the run is finished, the application deletes its claim:
1kubectl delete sandboxclaim customer-report-runThe right cleanup behavior depends on whether the caller needs speed, visible termination, or an audit record:
| Lifecycle setting | Use it when | What happens |
|---|---|---|
shutdownPolicy: Delete | Normal asynchronous cleanup is enough | The claim is deleted and deletion cascades to its Sandbox |
DeleteForeground | The caller must observe that all owned resources have stopped | The claim remains terminating until the Sandbox and workload are gone |
Retain | Claim status should remain as a record | Compute is removed, but the expired or finished claim object remains |
shutdownTime | The environment must not run beyond an absolute deadline | Cleanup begins at the supplied RFC3339 time |
ttlSecondsAfterFinished | A finite script may finish before the agent explicitly deletes it | Cleanup begins the configured number of seconds after the finished condition |
These safeguards matter when an agent process crashes or loses its connection before releasing the claim. Regardless of the selected policy, the warm pool maintains clean capacity for the next request; the used environment is not returned to another user.
Workflow Variation: Suspend an Idle Workspace
A short code-execution request should normally release its claim. A long-lived Python workspace may instead need to preserve its identity while stopping active compute. A directly managed Sandbox can switch between Running and Suspended:
1kubectl patch sandbox python-script-runner --type=merge \
2 -p '{"spec":{"operatingMode":"Suspended"}}'
3
4kubectl patch sandbox python-script-runner --type=merge \
5 -p '{"spec":{"operatingMode":"Running"}}'Suspension terminates the active Pod. Resuming asks the controller to make the workload ready again, so it is not the same millisecond path as adopting an already-running warm Sandbox. Persistent storage is needed for data that must survive this transition.
What Does service Do?
Set service: true when another workload needs to connect to the Sandbox through a stable in-cluster hostname:
1spec:
2 service: trueThe controller creates a headless Kubernetes Service and reports its name and fully qualified domain name in Sandbox.status.service and Sandbox.status.serviceFQDN.
This is useful when a Sandbox exposes an HTTP server, notebook, language server, or another long-running endpoint. Clients use the stable Service name instead of tracking Pod IP changes.
| Value | Behavior |
|---|---|
true | Create and manage a headless Service |
false | Do not create a Service; remove one managed by the Sandbox |
| Omitted | Preserve an existing Service, but do not create a new one |
API-server operations such as exec do not need this Service. Keep service: false when the Sandbox does not accept network connections.
Network access is a separate concern. service: true gives the workload a discoverable name; it does not grant ingress through a NetworkPolicy or authorize a caller.
How Should Sandboxes Use Storage?
Choose the storage pattern based on whether data belongs to one Sandbox, many Sandboxes, or one exceptional request.
A Separate Volume for Every Sandbox
Use volumeClaimTemplates on the SandboxTemplate when every environment needs its own private workspace:
1spec:
2 podTemplate:
3 spec:
4 containers:
5 - name: python
6 volumeMounts:
7 - name: workspace
8 mountPath: /workspace
9 volumeClaimTemplates:
10 - metadata:
11 name: workspace
12 spec:
13 accessModes: ['ReadWriteOnce']
14 resources:
15 requests:
16 storage: 1GiEach Sandbox receives a different PVC from the same template. The pool provisions it while warming the environment, so the workspace is ready when claimed. This is the normal choice for private agent files, generated code, and per-user state.
One Existing Volume Shared by Sandboxes
Reference an existing PVC in podTemplate.spec.volumes when several Sandboxes should see the same data:
1spec:
2 podTemplate:
3 spec:
4 containers:
5 - name: python
6 volumeMounts:
7 - name: shared-datasets
8 mountPath: /datasets
9 readOnly: true
10 volumes:
11 - name: shared-datasets
12 persistentVolumeClaim:
13 claimName: shared-datasetsThis is useful for common reference data or model artifacts. The PVC and storage backend must support the required access mode and concurrent mounts—commonly ReadOnlyMany or ReadWriteMany for Sandboxes that may run on different nodes. Prefer a read-only mount when workloads only consume the shared data.
A Different Volume for One Request
Put a volume on the SandboxClaim when one request needs a different size, storage class, or workspace definition:
1spec:
2 warmPoolRef:
3 name: python-agent-pool
4 volumeClaimTemplates:
5 - metadata:
6 name: large-output
7 spec:
8 accessModes: ['ReadWriteOnce']
9 resources:
10 requests:
11 storage: 10GiClaim-level volumes must be permitted by the template’s volumeClaimTemplatesPolicy. Disallowed rejects them, Allowed permits new names, and Overrides also permits replacing a template entry with the same name.
This customization causes a cold start because an already-running pool member cannot gain a different pre-created storage layout. Keep the normal workspace in the template and use claim-level storage only for exceptional requests.
Security Is Still Layered
Calling the resource a Sandbox does not automatically make it a security boundary. The strength of isolation comes from the complete cluster configuration.
For agent code execution, consider at least:
- Running as a non-root user and dropping Linux capabilities
- Disabling privilege escalation and service-account token mounting
- Setting CPU, memory, and storage limits
- Applying explicit ingress and egress policy
- Using trusted, minimal images and read-only filesystems where possible
- Restricting who can create claims or use
exec - Applying admission, quota, audit, and cleanup policies
The template in this post starts with a minimal Python Alpine image, no mounted service-account token, a non-root user, resource limits, and denied network traffic. Adjust those controls for the scripts and cluster you actually operate.
Getting Started
You need a Kubernetes cluster, kubectl, and permission to install custom resources and controllers. Pin a released version rather than using a moving target. The core controller and extensions are published as separate manifests:
1export VERSION='v0.5.2'
2
3# Install the core Sandbox API and controller
4kubectl apply -f \
5 "https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${VERSION}/sandbox.yaml"
6
7# Install SandboxTemplate, SandboxWarmPool, and SandboxClaim
8kubectl apply -f \
9 "https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${VERSION}/extensions.yaml"The core API provides Sandbox; the extensions provide SandboxTemplate, SandboxWarmPool, and SandboxClaim.
Verify the installation, apply the template and pool from this post, and watch the ready capacity:
1kubectl api-resources | grep -i sandbox
2kubectl apply -f python-agent-platform.yaml
3kubectl get sandboxwarmpools,sandboxes,podsThe examples focus on the fields that change how an agent workload behaves. For every API field, default, condition, and status value, use the authoritative Agent Sandbox v1beta1 API reference. Embedded objects such as podTemplate.spec, PVC specifications, and network rules use the normal Kubernetes API.
Closing Thought
Agent Sandbox is easiest to understand as an allocation lifecycle around a Pod:
1Template defines it
2Warm Pool prepares it
3Claim requests it
4Sandbox owns it
5Pod runs itThat small shift—from managing replicas to managing ready environments—makes the API a natural fit for agent code execution, interactive workspaces, notebooks, and other one-to-one workloads. The application gets a simple claim API, the platform keeps the environment consistent, and Kubernetes continues handling scheduling, storage, networking, and policy underneath it.