# Templates

A template is a reusable starting point for new sandboxes. Every template is stored as a RAM snapshot of a booted VM, so a sandbox created from it starts from a ready system instead of booting (target p95 < 500 ms).

## Official templates

| Template | Contents |
| --- | --- |
| `base` | Ubuntu 24.04, systemd, Docker, Python 3.12, common build tools; user `user` with passwordless sudo |
| `python`, `node`, `browser`, `coding-agent`, `jupyter` | language runtimes, a headless browser, coding-agent tools or JupyterLab, ready in one call; see the [Template gallery](../templates.md) |
| `desktop` | `base` + XFCE, KasmVNC, Chromium, Firefox, xdotool and fonts; desktop is already running. Needs size `default` or larger |

`template` defaults to `base` when you create a sandbox without a source.

## Your own templates

Create a template from **either** an OCI/Docker image **or** a snapshot.

### From an OCI image

We pull the image (linux/amd64), turn it into a VM root filesystem and add our guest agent. If the image has systemd, the agent and `start_command` run as systemd services; if it doesn't, a small init (`taskcloud-init`) starts the agent and your `start_command` (or the image's ENTRYPOINT/CMD). We then boot it, wait for `ready_check` to exit 0, and take a RAM snapshot. The call returns `202` with state `building`.

```ts
const tpl = await client.templates.create({
  name: "agent-env",
  image: { ref: "ghcr.io/acme/agent-env:1.4", registry_auth: { username: "acme-bot", password: process.env.GHCR_TOKEN! } },
  start_command: "/usr/local/bin/warmup.sh",
  ready_check: { command: "curl -sf localhost:8080/health", timeout_seconds: 120 },
  size: "default",
});
```

```python
tpl = client.templates.create(
    name="agent-env",
    image={"ref": "ghcr.io/acme/agent-env:1.4",
           "registry_auth": {"username": "acme-bot", "password": os.environ["GHCR_TOKEN"]}},
    start_command="/usr/local/bin/warmup.sh",
    ready_check={"command": "curl -sf localhost:8080/health", "timeout_seconds": 120},
    size="default",
)
```

```bash
curl -sS -X POST https://api.task.cloud/v1/templates \
  -H "Authorization: Bearer $TASKCLOUD_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"agent-env","image":{"ref":"ghcr.io/acme/agent-env:1.4"},"size":"default"}'

taskcloud template create --name agent-env --image ghcr.io/acme/agent-env:1.4
taskcloud template logs agent-env --follow
```

Follow the build with `GET /templates/{template_id}/logs` (plain text, or SSE with `Accept: text/event-stream`). The template becomes `ready` or `failed` (see `error`).

Image build options:

| Field | Notes |
| --- | --- |
| `image.ref` | OCI reference, linux/amd64 |
| `image.registry_auth` | `username`, `password` (write-only) for private registries |
| `start_command` | started before the snapshot |
| `ready_check` | `command` that must exit 0; `timeout_seconds` default 300, max 1800 |
| `env` | baked into the template |
| `desktop` | `true` adds the XFCE + KasmVNC desktop stack (coming soon for custom images; use the `desktop` base template today) |
| `size` | size used for the build snapshot |

### From a snapshot

Set up a sandbox by hand or with exec, take a named snapshot, then turn it into a template. It is `ready` immediately (`201`) and keeps that exact RAM and disk state.

```ts
const snap = await sandbox.snapshots.create({ name: "python-preinstalled" });
await client.templates.create({ name: "python-preinstalled", snapshot_id: snap.id });
```

```bash
taskcloud template create --name python-preinstalled --snapshot snap_3kd7wq2mzn5x
```

## Versions

Creating a template with an existing `name` adds a new `version`. `template: "agent-env"` uses the latest `ready` version; pin one with `template: "agent-env:3"`. Deleting a template does not affect sandboxes already created from it.

```ts
const sbx = await client.sandboxes.create({ template: "agent-env:3" });
```

## Templates vs. images vs. snapshots

- Use an **image** directly (`POST /sandboxes` with `image`) for one-offs. Each create converts and boots the image, so it is slower than a template.
- Use a **template** for anything you start often.
- Use a **snapshot** to continue from one specific sandbox's state. See [Snapshots and fork](snapshots-and-fork.md).

Template storage is billed like snapshot storage. See [Billing](../billing.md).

## Related

- [Sandboxes](sandboxes.md)
- [Desktop and computer use](../guides/desktop-and-computer-use.md)
- [API reference](../api-reference.md)
