Documentation/docs/template

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

TypeDescriptionVisibility
BuiltinSystem-provided templates managed by the platform operatorPublic — visible to all teams
CustomTemplates created and owned by your teamPrivate — 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.

POST

/api/v1/templates

Request Body#

FieldTypeDescription
template_idstringUnique identifier for the template (e.g., my-python-env)
specobjectFull 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):

yaml
spec: mainContainer: image: python:3.12-slim resources: cpu: "1" memory: 2Gi pool: minIdle: 2 maxIdle: 10

Example with a template-started warm process:

yaml
spec: 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.

go
tpl, 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)
go
tpl, 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.

GET

/api/v1/templates/{id}

go
tpl, 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.

GET

/api/v1/templates

go
templates, 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.

PUT

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

go
tpl, 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.

DELETE

/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