> ## Documentation Index
> Fetch the complete documentation index at: https://docs.boat.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Templates

> Save a sandbox under a name, deploy many sandboxes from it, and share it with another account.

A template is a sandbox that you save under a name. This saved copy is a **named snapshot**. Each new sandbox that you deploy from it starts with the same disk.

Use a template when many sandboxes need the same tools. You install the tools one time. You do not install them in each new sandbox.

## Automatic and named snapshots

| | Automatic snapshot | Named snapshot |
| - | - | - |
| Who makes it | Boat, every minute and at each stop | You, with `boat snapshot <id> <name>` |
| Belongs to | One sandbox | Your account or your organization |
| Kept | For the life of the sandbox. New ones replace old ones | Until you remove it, even after the sandbox is gone |
| Used by | `boat resume`, `boat fork`, `boat snapshot pull` | `boat new --from <name>` |
| Price | Free, no limit | 10 free, then \$1.70 a month each ([details](#named-snapshot-pricing)) |

## Make a template

1. Create a sandbox. Install everything: runtimes, packages, your app.
2. Save it with `boat snapshot <id> <name>`. This saves the disk as it is now, under the name.
3. Deploy each new sandbox with `boat new --from <name>`.

Each deploy is ready in a few seconds. The time and the cost stay about the same, whatever the template holds.

<CodeGroup>
  ```bash CLI theme={null}
  boat new                            # install your stack, then:
  boat snapshot current web-stack     # freeze it under a name
  boat new --from web-stack           # deploy as many as you want
  boat snapshots                      # your named snapshots, then capture history
  boat snapshot rm web-stack          # remove it and release its storage
  ```

  ```bash curl theme={null}
  curl -sS -X POST "$BOAT_API_BASE/named-snapshots" \
    -H "Authorization: Bearer $BOAT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"sandboxId":"bx_23456789","name":"web-stack"}'

  curl -sS -X POST "$BOAT_API_BASE/sandboxes" \
    -H "Authorization: Bearer $BOAT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"from":"web-stack","environment":"users","env":{"TENANT_ID":"acme"}}'
  ```

  ```ts TypeScript theme={null}
  await sandbox.saveNamedSnapshot({ sandboxId: "bx_23456789", name: "web-stack" });

  // poll until it settles at "ready"
  const saved = await sandbox.getNamedSnapshot({ name: "web-stack" });

  await sandbox.create({ from: "web-stack", environment: "users", env: { TENANT_ID: "acme" } });

  await sandbox.listNamedSnapshots();
  await sandbox.deleteNamedSnapshot({ name: "web-stack" });
  ```

  ```python Python theme={null}
  from boat_sdk.models.named_snapshot_save_request import NamedSnapshotSaveRequest

  sandbox.save_named_snapshot(NamedSnapshotSaveRequest(sandbox_id="bx_23456789", name="web-stack"))

  # poll until it settles at "ready"
  saved = sandbox.get_named_snapshot("web-stack")

  sandbox.create(CreateSandboxRequest.from_dict({
      "from": "web-stack",
      "environment": "users",
      "env": {"TENANT_ID": "acme"},
  }))

  sandbox.list_named_snapshots()
  sandbox.delete_named_snapshot("web-stack")
  ```
</CodeGroup>

<Note>
  In Python, `from` is a reserved word. Build this request with `from_dict`, as above. Do not use keyword arguments.
</Note>

* If the sandbox runs, the save takes a new snapshot of its disk. This takes a moment.
* If the sandbox is stopped, the save uses its last snapshot.
* `boat snapshots` shows the size of each named snapshot.

### A template does not depend on its sandbox

A named snapshot is a full copy. The source sandbox can change, stop or be deleted. The name still deploys the exact disk that you saved.

A named snapshot does not expire.

## Update a template

Save the same name again:

1. Resume the sandbox, or use any sandbox that has the setup you want.
2. Update your tools.
3. Run `boat snapshot <id> <name>` with the same name.

The name now deploys the new disk. Boat removes the old copy. Sandboxes that you already deployed do not change.

If the new save fails, the name keeps the last good save.

## Templates and environments

A template holds the **disk**. An [environment](/environments) holds the **settings**. Most setups use both.

| | Template | Environment |
| - | - | - |
| Holds | Installed packages, builds, caches | Repositories to clone, secrets, logins |
| Answers | "What is already on this machine?" | "What can this machine use?" |
| Costs | Storage, and a few seconds per deploy | Nothing |
| To change it | Save the name again. Deployed sandboxes do not change | Boat makes a new version. Running sandboxes change only when you upgrade them |
| Used at | Deploy, one time | Every start, resume and fork |

A simple rule: what you put in a Dockerfile goes in a template. What you put in a `.env` file goes in an environment. Use both together:

```bash theme={null}
boat new --from web-stack --environment users --env TENANT_ID=acme
```

This sandbox starts with your tools installed, none of your logins, and one variable of its own.

<Warning>
  A deploy from a template does **not** get the **named environment** of the source sandbox. It gets the environment from `--environment`, or your default environment. So a template that you built while logged in to your GitHub does not give that access to its deploys.

  A deploy **does** get the **per-sandbox variables** of the source, the same as a fork. Each `--env KEY=VALUE` that you gave the source is set on every deploy, unless the deploy sends its own `env`. Do not put a secret in `--env` on a sandbox that you will save as a template.
</Warning>

## Named snapshot pricing

Each account and each organization has 10 free named snapshots. You can keep more. Each named snapshot after the first 10 costs \$1.70 a month.

* Boat takes this cost each day, from your Boat time (about \$0.06 a day for each one). Boat uses your plan's included usage first, then your credit packs. Sandbox time works the same way.
* When you remove a named snapshot, it stops costing from the next day.
* A save goes to your active organization, or to the one that you pass with `--org`. All members of an organization share its 10 free named snapshots.
* If a member leaves an organization, they keep their named snapshots. These then count on the member's own account.
* To save a new named snapshot after the first 10, you need credit. Without credit, the save fails with `credit_required`.

`boat snapshots` shows how many named snapshots you keep and their monthly cost. The API gives the same numbers in `allowance` on `GET /named-snapshots`.

### When your balance is empty

1. Your sandboxes get the usual grace period.
2. Your named snapshots after the first 10 get 7 days.
3. Boat sends you an email. It lists these named snapshots and the date that Boat deletes them. Boat sends a reminder one day before.
4. If your balance is still empty on that date, Boat deletes them.

Boat never deletes your 10 most recent named snapshots.

To cancel the deletion, add funds. You can also remove named snapshots until you have 10 or fewer.

## Share a template with another account

A share code lets another Boat account deploy one of your named snapshots. The other account runs `boat new --from <code>`. The new sandbox is in their account, and they pay for it.

1. Create the source sandbox.
2. Install your tools and save them: `boat snapshot <id> <name>`.
3. Make a code: `boat snapshot share <name>`. Give the code to the other account.

<CodeGroup>
  ```bash CLI theme={null}
  boat snapshot share web-stack              # prints bsh_... one time only
  boat snapshot shares web-stack             # list codes, use counts, status
  boat snapshot unshare web-stack shr_...    # revoke one code

  # in the other account
  boat new --from bsh_...
  ```

  ```bash curl theme={null}
  curl -sS -X POST "$BOAT_API_BASE/named-snapshots/web-stack/shares" \
    -H "Authorization: Bearer $BOAT_API_KEY"

  curl -sS "$BOAT_API_BASE/named-snapshots/web-stack/shares" \
    -H "Authorization: Bearer $BOAT_API_KEY"

  curl -sS -X DELETE "$BOAT_API_BASE/named-snapshots/web-stack/shares/shr_..." \
    -H "Authorization: Bearer $BOAT_API_KEY"

  # in the other account
  curl -sS -X POST "$BOAT_API_BASE/sandboxes" \
    -H "Authorization: Bearer $BOAT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"from":"bsh_..."}'
  ```

  ```ts TypeScript theme={null}
  const { shareCode, share } = await sandbox.createSnapshotShare({ name: "web-stack" });

  await sandbox.listSnapshotShares({ name: "web-stack" });
  await sandbox.revokeSnapshotShare({ name: "web-stack", shareId: share.id });

  // in the other account
  await sandbox.create({ from: shareCode });
  ```

  ```python Python theme={null}
  created = sandbox.create_snapshot_share("web-stack")

  sandbox.list_snapshot_shares("web-stack")
  sandbox.revoke_snapshot_share("web-stack", created.share.id)

  # in the other account
  sandbox.create(CreateSandboxRequest.from_dict({"from": created.share_code}))
  ```
</CodeGroup>

* Boat shows the code one time only, when you make it. Boat keeps only a hash of the code.
* If you lose a code, revoke it and make a new one.
* A code has 256 random bits. Nobody can guess it.

<Warning>
  Before the other account can use its sandbox, Boat removes these from the disk: your logins and API keys, the secret files of your environment, your repository clones, and your SSH authorized keys.

  Boat shares everything else on the disk. This includes secrets that you wrote into files yourself.
</Warning>

The other account gets:

* A sandbox in their account, which they pay for.
* None of your per-sandbox variables. Their own environment applies, as with any `boat new`. To use no environment, they send `noEnv: true` (`--no-env`).

What happens to a code over time:

| You do this | Result |
| - | - |
| Save the same name again | The code deploys the new save |
| Revoke the code | New deploys with the code fail. Sandboxes made before keep their copy |
| Remove the name | Boat removes its codes too. Sandboxes made before keep their copy |
| Delete your account | Sandboxes made from your codes keep their copy |

A wrong or revoked code fails with `404 snapshot_not_found`.

API keys need these scopes:

| Action | Scope |
| - | - |
| Make or revoke a code | `snapshot.write` |
| List codes | `snapshot.read` |
| Deploy from a code | `box.create` and `snapshot.read`, the same as any `from` |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.