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 Pod

The 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 here

A 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-alpine

The 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-alpine

SandboxWarmPool

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-template

SandboxClaim

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-pool

How 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: Disallowed

The 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: OnReplenish

This pool keeps two ready Python environments available. Empty ingress and egress lists in the template apply a default-deny policy in both directions.

Update strategyWhat happens after the template changesBest fit
OnReplenishExisting members stay; replacements use the new template as capacity is replenishedGradual updates without unnecessary churn
RecreateStale, unclaimed members are removed and rebuiltFaster 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: DeleteForeground

Labels 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:

RequestPathReason
Uses the template as-isWarmA matching Sandbox is already running
Adds only labels or annotationsWarmMetadata can be applied during allocation
Injects environment variablesColdThe container needs different configuration
Adds or overrides volume templatesColdStorage must be provisioned before startup
Arrives when the pool has no ready capacityColdA 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)})
5PY

In 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-run

The right cleanup behavior depends on whether the caller needs speed, visible termination, or an audit record:

Lifecycle settingUse it whenWhat happens
shutdownPolicy: DeleteNormal asynchronous cleanup is enoughThe claim is deleted and deletion cascades to its Sandbox
DeleteForegroundThe caller must observe that all owned resources have stoppedThe claim remains terminating until the Sandbox and workload are gone
RetainClaim status should remain as a recordCompute is removed, but the expired or finished claim object remains
shutdownTimeThe environment must not run beyond an absolute deadlineCleanup begins at the supplied RFC3339 time
ttlSecondsAfterFinishedA finite script may finish before the agent explicitly deletes itCleanup 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: true

The 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.

ValueBehavior
trueCreate and manage a headless Service
falseDo not create a Service; remove one managed by the Sandbox
OmittedPreserve 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: 1Gi

Each 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-datasets

This 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: 10Gi

Claim-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,pods

The 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 it

That 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.

Further Reading