#Template
A Template is the blueprint for creating Sandboxes. It defines the container image, resource limits, warm pool size, and default network policy. Every Sandbox is created from a Template.
Template Types#
Sandbox0 has two categories of templates:
| Type | Description | Visibility |
|---|---|---|
| Builtin | System-provided templates managed by the platform operator | Public — visible to all teams |
| Custom | Templates created and owned by your team | Private — visible to your team only (configurable) |
Builtin templates use curated public images and are ready to use immediately. Custom templates let you bring your own image, fine-tune resources, and declare warm processes that start as soon as the template pod enters the warm pool. Procd remains PID 1 and owns those processes. See Warm Processes for the dedicated guide.
Create Template#
Create a custom template for your team. The template spec is defined in a YAML file.
/api/v1/templates
Request Body#
| Field | Type | Description |
|---|---|---|
template_id | string | Unique identifier for the template (e.g., my-python-env) |
spec | object | Full template specification |
Template IDs must be unique within your team. Once created, the ID cannot be changed — create a new template if you need a different ID.
Example spec file (template.yaml):
yamlspec: mainContainer: image: python:3.12-slim resources: cpu: "1" memory: 2Gi pool: minIdle: 2 maxIdle: 10
Example with a template-started warm process:
yamlspec: mainContainer: image: ubuntu:24.04 resources: cpu: "1" memory: 2Gi warmProcesses: - name: codex type: cmd alias: codex command: ["/bin/sh", "-lc", "/app/start-codex.sh"] cwd: /workspace envVars: MODE: warm probes: readiness: process: {} pool: minIdle: 2 maxIdle: 10
Warm processes start during template pod creation, before the pod is claimable from the warm pool. They are intended for long-running helpers such as agents, language servers, and daemon-style workers. SandboxProbe readiness keeps unhealthy helpers out of the warm pool, and SandboxProbe liveness hands restart back to Kubernetes by terminating procd on failure.
gotpl, err := client.CreateTemplate(ctx, apispec.TemplateCreateRequest{ TemplateID: "my-codex-env", Spec: apispec.SandboxTemplateSpec{ MainContainer: apispec.NewOptContainerSpec(apispec.ContainerSpec{ Image: "ubuntu:24.04", Resources: apispec.ResourceQuota{ CPU: apispec.NewOptString("1"), Memory: apispec.NewOptString("4Gi"), }, }), WarmProcesses: []apispec.WarmProcessSpec{ { Name: apispec.NewOptString("codex"), Type: apispec.WarmProcessSpecTypeCmd, Alias: apispec.NewOptString("codex"), Command: []string{"/bin/sh", "-lc", "/app/start-codex.sh"}, CWD: apispec.NewOptString("/workspace"), EnvVars: map[string]string{"MODE": "warm"}, }, }, Pool: apispec.NewOptPoolStrategy(apispec.PoolStrategy{ MinIdle: 2, MaxIdle: 10, }), }, }) if err != nil { log.Fatal(err) } fmt.Printf("Template created: %s\n", tpl.TemplateID)
gotpl, err := client.CreateTemplate(ctx, apispec.TemplateCreateRequest{ TemplateID: "my-python-env", Spec: apispec.SandboxTemplateSpec{ MainContainer: apispec.NewOptContainerSpec(apispec.ContainerSpec{ Image: "python:3.12-slim", Resources: apispec.ResourceQuota{ CPU: apispec.NewOptString("1"), Memory: apispec.NewOptString("2Gi"), }, }), Pool: apispec.NewOptPoolStrategy(apispec.PoolStrategy{ MinIdle: 2, MaxIdle: 10, }), }, }) if err != nil { log.Fatal(err) } fmt.Printf("Template created: %s\n", tpl.TemplateID)
Get Template#
Retrieve a specific template by ID. Your team can access both its own templates and builtin templates.
/api/v1/templates/{id}
gotpl, err = client.GetTemplate(ctx, "my-python-env") if err != nil { log.Fatal(err) } fmt.Printf("Template: %s (scope: %s)\n", tpl.TemplateID, tpl.Scope)
List Templates#
List all templates visible to your team — including your team's custom templates and all public builtin templates.
/api/v1/templates
gotemplates, err := client.ListTemplate(ctx) if err != nil { log.Fatal(err) } for _, tpl := range templates { display, _ := tpl.Spec.DisplayName.Get() fmt.Printf("- %s (%s) scope=%s\n", tpl.TemplateID, display, tpl.Scope) }
Update Template#
Update the spec of an existing custom template. The update triggers a reconciliation across all clusters — running sandboxes are not affected.
/api/v1/templates/{id}
Updating a template does not affect already-running Sandboxes. New Sandboxes claimed after the update will use the new spec. Idle pods in the pre-warm pool are recycled and replaced with pods matching the new spec.
gotpl, err = client.UpdateTemplate(ctx, "my-python-env", apispec.TemplateUpdateRequest{ Spec: apispec.SandboxTemplateSpec{ MainContainer: apispec.NewOptContainerSpec(apispec.ContainerSpec{ Image: "python:3.12-slim", Resources: apispec.ResourceQuota{ CPU: apispec.NewOptString("2"), Memory: apispec.NewOptString("4Gi"), }, }), Pool: apispec.NewOptPoolStrategy(apispec.PoolStrategy{ MinIdle: 3, MaxIdle: 10, }), }, }) if err != nil { log.Fatal(err) } fmt.Printf("Template updated: %s\n", tpl.TemplateID)
Delete Template#
Delete a custom template. All cluster allocations for the template are cleaned up via reconciliation.
/api/v1/templates/{id}
Deleting a template does not terminate running Sandboxes created from it, but no new Sandboxes can be created from the deleted template.
go_, err = client.DeleteTemplate(ctx, "my-python-env") if err != nil { log.Fatal(err) } fmt.Println("Template deleted")
Next Steps#
Custom Images
Build and push private container images for your templates
Warm Processes
Start helper processes before a warm pod is claimable
Warm Pool
Pre-warm idle sandboxes for sub-200ms cold start
Configuration
Full reference for all template spec fields
Sandbox
Create sandboxes from templates