1
0
mirror of https://github.com/docker/docs.git synced 2026-10-23 18:09:02 +03:00

docs: document v3 kits (#26065)

Add v3 kit documentation for Docker Sandboxes: concepts, usage,
authoring, and kit sets. Link to the upstream specification for
reference details and retain v2 guidance.

Validation: Markdown/style checks, Hugo build, internal links, and
example build checks passed.

Closes docker/sbx-releases#569

@netlify /ai/sandboxes/customize/
This commit is contained in:
David Karlsson
2026-09-24 18:00:08 +02:00
committed by GitHub
parent acff26fa65
commit 80bd6569e5
35 changed files with 2645 additions and 2034 deletions

View File

@@ -28,6 +28,9 @@ In this guide, you'll learn how to:
- Write an agent kit that wires Vibe to the Mistral API through the proxy
- Validate, launch, and iterate on the sandbox
This guide uses the v2 format. For its reference and migration
guidance, see [Kits v2](../manuals/ai/sandboxes/customize/kits-v2.md).
## How isolation works
Every outbound request from a sandbox passes through a proxy that runs on
@@ -168,7 +171,7 @@ Each field does the following:
| Field | Purpose |
| --------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `kind: sandbox` | Declares a sandbox agent: a complete image plus its launch configuration. |
| `name` | The kit's identifier, reused in the `sbx run` command. |
| `name` | The kit's identifier. Pass the kit directory to `sbx run`. |
| `sandbox.image` | The pinned image you published in Step 3. Its `CMD` launches Vibe, so the kit doesn't set an entrypoint. |
| `agentInstructions.filename`| The instructions file Vibe reads in the project. |
| `agentInstructions.content` | Markdown appended to `AGENTS.md` at creation to prime the agent about its environment. |
@@ -177,9 +180,6 @@ Each field does the following:
| `credentials[].apiKey.name` | The environment variable the proxy manages. Vibe sees a sentinel value; the proxy swaps in the real key. |
| `credentials[].apiKey.inject`| Where and how the proxy attaches the key. `scheme: bearer` sets `Authorization: Bearer <key>` for the domain. |
For the full kit format, see
[Kits](../manuals/ai/sandboxes/customize/kits.md).
> [!WARNING]
> `--agent auto-approve` runs Vibe in a mode that approves every tool
> execution without prompting. The sandbox isolates the agent from your host,
@@ -197,13 +197,13 @@ $ sbx kit validate ./mistral-vibe
Then, from your project directory, launch the agent with the kit:
```console
$ sbx run --kit ./mistral-vibe --name mistral-vibe mistral-vibe .
$ sbx run ./mistral-vibe --name mistral-vibe .
```
- `--kit ./mistral-vibe` points to the folder that contains `spec.yaml`.
- `./mistral-vibe` is the sandbox kit reference, pointing to the folder that
contains `spec.yaml`.
- `--name mistral-vibe` names the sandbox. Without it, `sbx` derives a name
from the agent and the working directory, and the commands below won't match.
- `mistral-vibe` is the agent name from `spec.yaml`.
- `.` is the project directory to mount in the sandbox.
Vibe starts in an isolated microVM, talks to the Mistral API through the
@@ -223,7 +223,7 @@ it. Use it to spot a host missing from `permissions.network.allow`. After you
change `spec.yaml`, recreate the sandbox for a clean start:
```console
$ sbx rm mistral-vibe && sbx run --kit ./mistral-vibe --name mistral-vibe mistral-vibe .
$ sbx rm mistral-vibe && sbx run ./mistral-vibe --name mistral-vibe .
```
## Clean up
@@ -245,7 +245,7 @@ Files in your workspace are unaffected.
## Learn more
- [Get started with Docker Sandboxes](../manuals/ai/sandboxes/get-started.md)
- [Build your own agent kit](../manuals/ai/sandboxes/customize/build-an-agent.md)
- [Customize sandboxes with kits](../manuals/ai/sandboxes/customize/kits.md)
- [Build a v3 agent workload](/manuals/ai/sandboxes/customize/author/build-an-agent.md)
- [Customize sandboxes with kits](/manuals/ai/sandboxes/customize/_index.md)
- [Credentials and built-in services](../manuals/ai/sandboxes/configuration/credentials.md#built-in-services)
- [Mistral Vibe](https://github.com/mistralai/mistral-vibe)

View File

@@ -48,8 +48,8 @@ jump to the [usage guide](usage.md) for basic commands.
Cursor to a sandbox over SSH
- [MCP gateway](mcp-gateway.md) — register MCP servers and connect them to
sandboxed agents
- [Customize](customize/) — reusable templates and declarative kits for
extending or tailoring sandboxes
- [Kits](customize/) — package tools and configuration into reusable
sandbox environments
- [Architecture](architecture.md) — microVM isolation, workspace mounting,
networking
- [Security](security/) — isolation model, credential handling, and

View File

@@ -124,7 +124,7 @@ See [Git workflows](../workflows/git.md) for clone-mode details.
## Base image
The sandbox uses `docker/sandbox-templates:claude-code`. See
[Templates](../customize/templates.md) to build your own image on top of
[Base images](/manuals/ai/sandboxes/customize/author/base-images.md) to build your own image on top of
this base.
## Use a local model

View File

@@ -69,5 +69,5 @@ $ sbx run --name <sandbox-name> -- run --yolo agent.yml
## Base image
The sandbox uses `docker/sandbox-templates:docker-agent`. See
[Templates](../customize/templates.md) to build your own image on top of
[Base images](/manuals/ai/sandboxes/customize/author/base-images.md) to build your own image on top of
this base.

View File

@@ -52,7 +52,7 @@ $ sbx secret set openai
Once inside the shell, you can install agents using their standard methods,
for example `npm install -g @continuedev/cli`. For complex setups, build a
[custom template](../customize/templates.md) instead of installing
[workload kit](/manuals/ai/sandboxes/customize/_index.md) instead of installing
interactively each time.
## Base image

View File

@@ -27,7 +27,7 @@ sees only a sentinel like `proxy-managed`.
A kit can set OAuth `passthrough: true` to opt out of sentinel masking. This
sends the real token response into the sandbox and reduces credential isolation.
See the [`oauth` kit fields](../customize/kit-reference.md#oauth).
See the [`oauth` kit fields](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/capabilities/com.docker.sandbox/credential@1.md).
There are several ways to provide that value. When more than one source has a
value for the same service, the stored secret takes precedence.
@@ -37,9 +37,12 @@ value for the same service, the stored secret takes precedence.
| [Stored secrets](#stored-secrets) (`sbx secret set`) | A value or dynamic source in your OS keychain, keyed by service | The default for any built-in or kit-declared service |
| [Custom secrets](#custom-secrets) (`sbx secret set-custom`) | A value keyed to a domain and environment variable | The service model doesn't fit — the agent validates the variable's format, or the secret rides in a request body |
| OAuth | A host-side sign-in flow; the token never enters the sandbox | The agent supports it, such as Claude Code, Codex, Cursor, or Droid |
| [Credential bindings](#credential-bindings) (`credentials.yaml`) | Per-service mechanism and domain approval | Required for third-party `schemaVersion: "2"` kits |
| [Registry credentials](#registry-credentials) (`sbx secret set --registry`) | Authentication for pulling images and kits | Pulling templates or kits from a private registry |
Providing a value and approving its use are separate steps.
[Credential bindings](#credential-bindings) record which mechanisms and domains
you authorize a kit to use. They don't store the credential value.
For multi-provider agents (OpenCode, Docker Agent), the proxy selects
credentials based on the API endpoint being called. See individual
[agent pages](../agents/) for provider-specific details.
@@ -227,8 +230,64 @@ it into requests to the listed API domains.
### Services declared by kits
Custom kits can declare their own service identifiers in `spec.yaml`. In
`schemaVersion: "2"`, credentials are declared under the `credentials:` list:
Use the service identifier from the kit's documentation when storing its
credential. For a kit that declares `my-service`, run:
```console
$ sbx secret set my-service
```
There's no separate registration step. The stored value and the kit's request
use the same identifier. Approve the kit's credential request when prompted.
See [Credential bindings](#credential-bindings).
When authoring a kit, declare how it uses that service. V3 uses a credential
capability, and v2 uses a top-level `credentials` list. Both examples declare the
service and permit access to its API host:
{{< tabs >}}
{{< tab name="v3" >}}
```yaml
capabilities:
- type: com.docker.sandbox/network-policy@1
config:
runtime:
allow: [api.my-service.com]
- type: com.docker.sandbox/credential@1
config:
service: my-service
phase: runtime
apiKey:
name: MY_SERVICE_TOKEN
proxyManaged: true
inject:
- domain: api.my-service.com
header: Authorization
format: "Bearer %s"
```
For API keys, specify both the HTTP header and its value format, as in this
example. `sbx` doesn't use `apiKey.inject[].scheme` in v3 kits.
For OAuth, use JSON credential files and a provider that returns the refresh
token in `refresh_token`. `sbx` doesn't support TOML credential files or a
different refresh-token field.
Credentials declared for the install phase are available during install hooks.
Add the domains that receive these credentials to the network policy's
`install.allow` list. An entry in `runtime.allow` alone doesn't grant access
during installation. If a hook reads a credential environment variable,
include that name in its `env` list.
In `sbx`, the proxy stops injecting credentials into install-only domains
before the agent launches. A domain remains available for credential injection
if any runtime credential targets it, even under a different service name.
If you declare the same service for both phases, `sbx` uses the runtime
declaration's credential settings in both phases.
{{< /tab >}}
{{< tab name="v2" >}}
```yaml
credentials:
@@ -239,21 +298,20 @@ credentials:
inject:
- domain: api.my-service.com
scheme: bearer
permissions:
network:
allow: [api.my-service.com]
```
{{< /tab >}}
{{< /tabs >}}
Each service declares `apiKey`, `oauth`, or both. When both resolve at runtime,
the API key takes precedence and OAuth acts as the fallback. To provide the
credential value, run `sbx secret set` with the same identifier the kit
declares:
```console
$ sbx secret set my-service
```
There's no separate registration step; the keychain entry is keyed on the
identifier the kit already uses. See
[Authenticate to external services](../customize/kits.md#authenticate-to-external-services)
for the full kit-side wiring.
the API key takes precedence and OAuth acts as the fallback. For complete
kit-side configuration, see
[V3 credential definition](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/capabilities/com.docker.sandbox/credential@1.md)
or [V2 credentials](../customize/kits-v2.md#credentials).
### List and remove secrets
@@ -460,11 +518,11 @@ you've approved for each service. It lives at
`~/.config/sbx/credentials.yaml`, or `%APPDATA%\sbx\credentials.yaml` on
Windows.
Third-party kits that declare `schemaVersion: "2"` require an approved binding
for each credential they use. `sbx` creates one interactively the first time you
Third-party kits require an approved binding for each credential they use,
regardless of schema version. `sbx` creates one interactively the first time you
run such a kit (see [First-run approval](#first-run-approval)); you can also
write entries by hand. Credentials declared only by embedded, built-in kits are
authorized by provenance and don't need a binding.
write entries by hand. Credentials requested only by the built-in kits shipped
with `sbx` don't need a binding.
Each entry under `bindings` is keyed by a
[service identifier](#built-in-services) and approves one or both credential
@@ -504,30 +562,31 @@ both cases, you approve the domains declared by the kit. `sbx` writes the entry
to `credentials.yaml`.
In non-interactive contexts (CI or `--detached`), there's no one to answer the
prompt. Without a binding, the sandbox starts with the credential withheld. If
the kit marks the credential as `required: true`, `sbx` also prints a warning.
prompt. In `sbx`, the sandbox starts with the credential withheld when no
binding exists. For a required credential, `sbx` prints a warning rather than
failing sandbox creation. This is a limitation of `sbx` enforcement: kit authors
shouldn't rely on a required credential being available merely because the
sandbox started.
Pre-create the binding by running the kit interactively once or by writing
`credentials.yaml` directly before running unattended.
The bindings file gates whether a third-party v2 kit can use a service
The bindings file gates whether a third-party kit can use a service
credential. The kit's credential injection rules and network permissions still
constrain which requests can carry the credential.
### Kits that require a binding
Only third-party kits that declare `schemaVersion: "2"` require a binding.
Built-in agents also use `schemaVersion: "2"`, but credentials declared only by
embedded kits are authorized by provenance and inject automatically. A
third-party kit that extends a built-in agent inherits its credentials, but not
its built-in provenance. The inherited credentials therefore require approval.
If a third-party kit declares the same service itself, that service also
requires approval. Kits on `schemaVersion: "1"` inject their declared
credentials without a binding.
Third-party kits require your approval to use credentials, regardless of
schema version. The built-in kits shipped with `sbx` can use credentials they
request without a binding. If a third-party v2 kit extends a built-in agent,
you must approve its use of the inherited credentials. You must also approve
a third-party kit that requests the same service itself.
## Registry credentials
Registry credentials authenticate to private OCI registries when pulling
[templates](../customize/templates.md) or [kits](../customize/kits.md), and can
[templates](../usage.md#load-a-template) or [kits](/manuals/ai/sandboxes/customize/_index.md), and can
also let the agent pull and push images from inside the sandbox through the
host-side proxy. Use `sbx secret set --registry <host>` to store them. For
Docker Hub, `sbx` reuses your `sbx login` session — no registry secret needed.
@@ -587,10 +646,12 @@ To scope the credential to a single sandbox, store it under that sandbox's name:
$ gh auth token | sbx secret set --sandbox my-app --registry ghcr.io --password-stdin
```
For Docker Hub, `sbx kit pull` and `sbx kit push` use the session from
For v2 kits on Docker Hub, `sbx kit pull` and `sbx kit push` use the session from
`sbx login`. For other registries, both commands use these credentials. Both
commands fall back to the Docker credential store, so credentials from
`docker login` also work.
`docker login` also work. V3 kits are
[published with Docker Buildx](/manuals/ai/sandboxes/customize/author/distribute.md#publish-an-image),
which uses the credentials from `docker login`.
### Trust a private registry authentication endpoint

View File

@@ -462,8 +462,15 @@ true`.
`kits` accepts local directories, ZIP archives, OCI registry references, and
Git URLs prefixed with `git+https://` or `git+ssh://`. Kits can install tools,
configure the sandbox, and give the agent project-specific instructions. See
[Kits](../customize/kits.md) for details.
configure the sandbox, and give the agent project-specific instructions.
The examples on this page pair built-in agents with v2 mixins. See
[Kits v2](/manuals/ai/sandboxes/customize/kits-v2.md) for that format, or
[Version compatibility](/manuals/ai/sandboxes/customize/_index.md#version-compatibility)
when selecting v3 kits.
An environment file selects kits and configures a sandbox's host resources.
A kit descriptor defines the package itself. The environment file's
`schemaVersion` is independent of the schema version in a kit descriptor.
Explicit relative paths resolve from the directory of the environment file
that declares them. These include `.`, `..`, paths that start with `./` or

View File

@@ -54,7 +54,7 @@ mirror URL paths differently from image repository prefixes.
Docker Engine connects to the mirror over HTTPS, so the sandbox must trust the
certificate that the mirror presents. For a mirror that uses an internal
certificate authority, add the CA to the sandbox's system trust store. See
[Install an internal CA certificate](../customize/kit-examples.md#install-an-internal-ca-certificate).
[Install an internal CA certificate](../customize/kits-v2.md#install-an-internal-ca-certificate).
Template and kit pulls use the changed setting immediately. Existing sandboxes
retain the Docker Engine mirror configuration with which they were created.

View File

@@ -305,7 +305,7 @@ organization's repositories, but not `github.com/myorg-other/`. The value
by [kit.allowLocalKits](#kitallowlocalkits), and pinned agent kits have the
[kit.allowExtractedAgents](#kitallowextractedagents) exception.
See [restrict kit sources](../customize/kits.md#restrict-kit-sources) for source
See [restrict kit sources](../customize/use-kits.md#restrict-kit-sources) for source
formats and examples.
#### kit.allowLocalKits {.wrap-anywhere}
@@ -366,7 +366,7 @@ Pinned agent kits remain exempt while
A signature covers the kit's `spec.yaml` and `files/` content. It doesn't pin
image tags or verify downloads performed by the kit's commands. See
[sign and verify kits](../customize/kits.md#sign-and-verify-kits).
[sign and verify kits](../customize/kits-v2.md#sign-and-verify-kits).
#### kit.trustedSigners {.wrap-anywhere}

View File

@@ -1,70 +1,115 @@
---
title: Customizing sandboxes
linkTitle: Customize
description: Build reusable sandbox images and extend or define agents using templates and kits.
keywords: sandboxes, sbx, customize, templates, kits, mixins, custom agents
title: Kits
description: Use sandbox kits and share environments as kit sets composed from workloads and mixins.
keywords: sandboxes, sbx, kits, v3, workloads, mixins
weight: 90
linkTitle: Kits
aliases:
- /ai/sandboxes/agents/custom-environments/
params:
sidebar:
badge:
color: blue
text: Early Access
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
Docker Sandboxes offers two ways to customize a sandbox beyond the built-in
defaults:
Kits let you shape a sandbox around the way you work. Use a custom base image,
add the tools your project needs, and give your agent instructions for using
them. You also control which services the sandbox can access, how it
authenticates, and what runs when the sandbox starts.
- [Templates](templates.md) — reusable sandbox images with tools, packages,
and configuration baked in. Extend a base image with a Dockerfile, or
save a running sandbox as a template.
- [Kits](kits.md) — declarative YAML artifacts that extend an agent with
tools, credentials, network rules, and files at runtime, or define a new
agent from scratch.
A kit can define the whole environment or add something to an existing one,
such as a toolchain or your team's shared configuration. Package those choices
once, then reuse them across projects and share them with your team.
Kits are experimental. The kit file format, CLI commands, and experience for
creating, loading, and managing kits are subject to change as the feature
evolves. Share feedback and bug reports in the
[docker/sbx-releases](https://github.com/docker/sbx-releases) repository.
This page covers v3 kits, which require `sbx` v0.45 or later. See
[Version compatibility](#version-compatibility) for compatibility with earlier
kit formats.
## Templates and kits, side by side
## What is a kit?
A template is a Docker image that the sandbox runs. It's built ahead
of time with a Dockerfile (or saved from a running sandbox), pushed to a
registry, and pulled when a sandbox is created. Use templates for things
that belong in an image: system packages, language toolchains, large
dependencies — anything you'd rather not reinstall on every sandbox start.
A kit packages software and configuration for a sandbox. A YAML file, called
its descriptor, tells Docker Sandboxes what the kit needs: network access,
credentials, setup commands, or instructions for the agent. You publish the
kit as a container image containing its files and descriptor.
A kit is a YAML artifact applied at sandbox creation. The kit can run
install commands, drop files into the sandbox, declare network and
credential rules, and (for sandbox kits) define which template image the
agent runs in. Use kits for things that vary per agent or per team:
shared linter config, project-specific install steps, credential
injection for a service the agent talks to.
You identify a published kit by its container image reference, such as
`me/my-kit:latest`. When you use the kit, `sbx` prepares its files and applies
its settings. You can use one kit for a complete environment or combine kits
that contribute different parts.
Templates and kits work together. A sandbox kit's `sandbox.image` field
points at a template: the template provides the base environment, the
kit layers config, secrets, and runtime behavior on top. A team can ship
one heavy template and several thin kits without rebuilding the image
each time something changes.
## What kits can do
## When to use which
Use kits to:
| Goal | Option |
| --------------------------------------------------------- | ------------------------------------------------------------- |
| Pre-install tools and packages into a reusable base image | [Template](templates.md) |
| Capture a configured running sandbox for reuse | [Saved template](templates.md#saving-a-sandbox-as-a-template) |
| Add a tool, credential, or config to agent runs via YAML | [Kit (mixin)](kits.md) |
| Define a new agent from scratch | [Kit (sandbox)](kits.md#define-an-agent) |
- Package a custom agent, or configure an existing agent for your team's
projects.
- Include the tools the agent needs, such as linters, language runtimes,
test runners, and compilers.
- Share linter rules, editor settings, helper scripts, and reference material.
Give the agent instructions and skills for using them.
- Connect the agent to services through network rules and credentials,
including internal APIs and private package registries.
- Initialize each sandbox and run supporting services when it starts, such
as a development server for previewing the agent's work.
Templates and kits can be used together. A template bakes heavy tools into
the image for fast sandbox startup; a kit layered on top adds per-run
credentials, config, or extra capabilities.
## Workloads and mixins
## Tutorials
Kits have two roles in a sandbox: a workload supplies the base environment
and launch command, and mixins add tools or behavior to it.
- [Build your own agent kit](build-an-agent.md) — step-by-step walkthrough
for packaging [Amp](https://ampcode.com/) as a sandbox kit.
| Kind | What it supplies | How you use it |
| --- | --- | --- |
| `workload` | The environment and command to run, such as an agent or a shell | Pass it to `sbx run` or `sbx create` |
| `mixin` | Additional tools, configuration, or runtime behavior | Add it with `--kit` |
For example, this command runs a workload with a mixin:
```console
$ sbx run me/my-agent-kit:latest --kit me/my-mixin:latest
```
The workload supplies the environment and launch command. The mixin adds its
tools and configuration to that environment.
## Kit sets
A kit set combines kits into a single kit that you can publish and reuse.
Use it to package a workload with the mixins you need, so you and your team
can run the environment from one reference.
The set specifies which kits and versions to combine. It can also add setup
commands, agent instructions, network access, and other settings for the
combined environment.
If the set includes a workload, you run the published kit with `sbx run`.
If it contains only mixins, you add it to a workload with `--kit`.
See [Use kits](/manuals/ai/sandboxes/customize/use-kits.md) for how to run kits
and add mixins. To customize and publish your own combination, see
[Compose a kit set](/manuals/ai/sandboxes/customize/author/kit-sets.md).
## Version compatibility
V3 kits cannot be combined with v1 or v2 kits in the same sandbox. To use
v3, select a v3 workload and use v3 for every mixin you add.
The built-in agent names, such as `claude` and `codex`, select v2 kits.
You can't add a v3 mixin to these built-ins. For example,
`sbx run claude --kit ./some-v3-mixin` fails because it mixes kit versions.
Instead, select a v3 workload by its published image, local path, or Git
reference, as shown in [Run a kit](/manuals/ai/sandboxes/customize/use-kits.md#run-a-kit).
V2 remains supported, including the built-in agents and
existing v2 customizations. See [Kits v2](/manuals/ai/sandboxes/customize/kits-v2.md) for maintenance
and migration guidance.
## Choose your next step
- [Use kits](/manuals/ai/sandboxes/customize/use-kits.md) to run a published environment or combine
a workload with mixins.
- [Author kits](/manuals/ai/sandboxes/customize/author/_index.md) to build an agent environment,
package a tool, or combine kits into a set to share with your team.
- Explore the [sandbox-kit-spec repository](https://github.com/docker/sandbox-kit-spec)
for the authoritative v3 specification, build frontend, and examples.
For the earlier format used by built-in agents, see [Kits v2](/manuals/ai/sandboxes/customize/kits-v2.md).
To save an environment you've configured inside a sandbox,
see [Save a sandbox as a template](/manuals/ai/sandboxes/usage.md#saving-a-sandbox-as-a-template).

View File

@@ -0,0 +1,188 @@
---
title: Author kits
description: Build and share sandbox environments with kits. Choose a tutorial, organize your source files, and decide what happens during build and setup.
keywords: sandboxes, sbx, kits, v3, workloads, mixins
weight: 20
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
Build a kit to give your team a repeatable sandbox environment. You can
package an agent, add a tool to use with different agents, or combine existing
kits into one kit your team can run. The guides in this section walk through
each approach.
> [!NOTE]
> Select a v3 workload and v3 mixins together.
> Built-in shortcuts such as `claude` and `codex` use v2 and can't be combined
> with v3 mixins. See [Version compatibility](/manuals/ai/sandboxes/customize/_index.md#version-compatibility)
> or the [v2 reference](/manuals/ai/sandboxes/customize/kits-v2.md).
## Choose what to author
- [Compose a kit set](/manuals/ai/sandboxes/customize/author/kit-sets.md) to combine
kits you've published or chosen from a registry. A set can also add network
access, setup commands, and agent instructions, and let users choose settings
such as which model to use.
- [Build a tool mixin](/manuals/ai/sandboxes/customize/author/tool-mixins.md) to
package a reusable tool with its network access and credentials.
- [Build an agent workload](/manuals/ai/sandboxes/customize/author/build-an-agent.md)
to control the base environment, agent installation, and launch command.
You can also [use an existing agent image](/manuals/ai/sandboxes/customize/author/base-images.md#use-an-image-in-a-v3-workload).
For complete kits you can study and adapt, see
[Kit examples](https://github.com/docker/sandbox-kit-spec/tree/main/examples)
in the Docker Sandbox Kit Specification repository.
## Directory and build layout
Keep each kit's source in its own directory. A kit usually starts with two
files:
- A YAML descriptor identifies the kit as a workload, mixin, or set and
declares its settings and requirements.
- A Dockerfile installs software and copies files into the image.
Use the kit's name for the directory and descriptor. If the kit has a
Dockerfile, use the same filename stem as the descriptor: `my-kit.yaml`
pairs with `my-kit.dockerfile`.
For example:
```text
my-kit/
├── my-kit.yaml
├── my-kit.dockerfile
├── context.md
└── files/
└── settings.json
```
Organize supporting files in whatever way suits your kit. This example keeps
agent instructions in `context.md` and configuration files under `files/`.
### How the files work together
The Dockerfile defines what goes into the image, such as installed tools
and configuration files. For a workload, it also sets the command to launch.
The YAML descriptor defines the kit's settings and requirements, such as
network access, credentials, and agent instructions.
The descriptor starts with a syntax declaration:
```yaml
# syntax=docker/sandbox-kit:3
```
This selects the kit build frontend, which reads the descriptor and its
matching Dockerfile. The build produces a container image containing the
software, supporting files, and validated descriptor.
A kit set uses the descriptor's `kits:` list to combine published kits.
### Build and use the kit
When building with Docker Buildx, pass the YAML descriptor to `-f` and
the source directory as the build context:
```console
$ docker buildx build -f my-kit/my-kit.yaml -t my-kit:dev my-kit/
```
You can also give `sbx` a reference to a local kit directory. It builds the kit
when creating the sandbox. After publishing the image to a registry, you can
use its image reference instead.
For build options and publishing instructions, see
[Build and distribute kits](/manuals/ai/sandboxes/customize/author/distribute.md).
### Other source layouts
The separate descriptor and Dockerfile are one way to organize a kit.
You can also write a Dockerfile inline under `build: |`, select one with
`dockerfile:`, or embed the descriptor in a Dockerfile comment.
See [Authoring forms](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/SPEC-v3.md#3-authoring-forms)
for the syntax.
## Capabilities
Installing a tool is often only part of the job. The tool might also need to
reach an API, authenticate with a credential, or run a setup command before
the agent starts. Describe those needs in the descriptor's `capabilities`
list. Each entry asks Docker Sandboxes to provide one of these features.
A kit's Dockerfile defines how its image is built. Its capabilities describe
what Docker Sandboxes needs to do when preparing and running the sandbox.
For example, a tool mixin can install an API client through its Dockerfile
and use a network capability to request access to that API. Building or
running the image with Docker alone doesn't apply these capability settings.
You can add capabilities to a workload, mixin, or set. Keep each request with
the kit that needs it, so a tool's access rules follow it when you use it with
another agent.
Each capability entry identifies the feature in `type`. Capabilities that
need settings take them in `config`. For example, `com.docker.sandbox/network-policy@1`
requests network access, and its `config` lists the domains to allow.
The `@1` identifies the capability's version. It is independent of the kit
format version and the `sbx` release. The
[upstream capability definitions](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/SPEC-v3.md#72-well-known-types)
describe the available capabilities and their settings.
> [!NOTE]
> `sbx` doesn't apply `usb-device@1`, `privileged@1`, or `agent-sessions@1`
> requests. Its capability enforcement can let sandbox creation succeed even
> when a required capability is unsupported. Don't rely on this behavior:
> choose capabilities supported by the runtime where your kit will run.
> The `kit-registry@1` capability is restricted to approved OCI builder kits;
> local and Git kit sources don't receive it.
The guides here show how to use capabilities with Docker Sandboxes. For all
descriptor fields and the rules for combining kits, see the
[upstream v3 specification](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/SPEC-v3.md).
## Choose when setup runs
Install tools and copy static files during the image build so you can reuse
them in every sandbox. Some setup needs to wait until the sandbox exists. For
example, a mixin can bring a CA certificate, but it needs the workload's tools
to register that certificate. Use a lifecycle capability to run commands or
generate files at the right point:
| Where to put the work | When it runs in `sbx` | Example |
| --- | --- | --- |
| Dockerfile `RUN` and `COPY` | Image build | Install a tool and copy its default configuration |
| Lifecycle `install` hooks | Once during sandbox creation, before the agent launches | Register a CA or populate a mounted directory |
| Lifecycle `files` | During creation, after install hooks and before the agent launches | Generate settings from kit arguments |
| Lifecycle `startup` hooks | Every sandbox start, alongside the agent | Start a background service or refresh state after a restart |
Lifecycle hooks are commands that run during sandbox creation or startup.
Make startup hooks safe to run more than once. In `sbx`, they run alongside
the agent, so the agent might start before they finish. If a command must
finish before every agent launch, put it in the workload's entrypoint instead.
### Pass environment variables to hooks
In `sbx`, install hooks receive a limited set of environment variables:
basic process variables, proxy settings, and certificate paths. If your command needs another
variable, name it in the hook's `env` list. For example, `env: [WORKSPACE_DIR]`
gives the command the mounted workspace's path. In `sbx`, startup hooks receive
the sandbox's environment without this filtering.
See [Kit authoring patterns](/manuals/ai/sandboxes/customize/author/patterns.md#run-setup-after-combining-kits)
for setup examples and the upstream
[lifecycle definition](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/capabilities/com.docker.sandbox/lifecycle@1.md)
for the fields.
## Set workload compute requirements
Set the workload's default CPU and memory allocation with the
[`resources@1` capability](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/capabilities/com.docker.sandbox/resources@1.md)
in its descriptor. Use a whole number of CPU cores. Anyone running the kit
can override these defaults with `--cpus` and `--memory` when creating a sandbox.
Put these settings on the workload. `sbx` ignores resource settings on
mixins added separately, and it doesn't use the capability's `gpu` field
to select GPUs.

View File

@@ -0,0 +1,190 @@
---
title: Base images for sandbox workloads
linkTitle: Base images
weight: 10
description: Choose a Docker-provided agent image or your own Linux base image for a sandbox workload kit.
keywords: sandboxes, sbx, kits, base images, templates, dockerfile, custom agents
aliases:
- /ai/sandboxes/customize/templates/
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
A base image gives your workload its operating system and starting set of
tools. Docker provides images with agents already installed. You can also start
from another Linux image and prepare it yourself. Choose the base image in your
workload's Dockerfile. Use the kit descriptor to configure network access,
credentials, and other sandbox behavior. A kit set uses the base image from its
workload.
To save and reuse an environment you've configured interactively, see
[Save a sandbox as a template](/manuals/ai/sandboxes/usage.md#saving-a-sandbox-as-a-template).
## Docker-provided images
Docker's sandbox templates are published as
`docker/sandbox-templates:<variant>`. They are based on Ubuntu and run as a
non-root `agent` user with sudo access. Most variants include Git, Docker
CLI, and common development tools like Node.js, Python, Go, and Java.
| Variant | Agent |
| --------------------- | -------------------------------------------------------------------- |
| `claude-code` | [Claude Code](https://claude.ai/download) |
| `claude-code-minimal` | Claude Code with a minimal toolset (no Node.js, Python, Go, or Java) |
| `codex` | [OpenAI Codex](https://github.com/openai/codex) |
| `copilot` | [GitHub Copilot](https://github.com/github/copilot-cli) |
| `cursor-agent` | [Cursor](https://cursor.com/cli) |
| `devin` | [Devin CLI](https://docs.devin.ai/work-with-devin/devin-cli) |
| `docker-agent` | [Docker Agent](https://github.com/docker/docker-agent) |
| `droid` | [Droid](https://www.factory.ai) |
| `gemini` | [Gemini CLI](https://github.com/google-gemini/gemini-cli) |
| `kiro` | [Kiro](https://kiro.dev) |
| `opencode` | [OpenCode](https://opencode.ai) |
| `shell` | No agent pre-installed. Use for manual agent setup. |
## Use an image in a v3 workload
Start your workload's Dockerfile with `FROM`, then add the tools and
configuration you need. Install system packages as `root`, and switch back
to `agent` before installing tools in the agent's home directory. Running
those installers as `root` puts files under `/root/`, where the agent can't
use them.
### What the base image provides
`FROM` inherits the image's files and settings. If the image is also a
published kit, its capabilities don't carry over. Declare network access,
credentials, storage, and hooks in your own descriptor. To keep an existing
kit's capabilities and add tools, [compose a kit set](/manuals/ai/sandboxes/customize/author/kit-sets.md).
Your descriptor determines the kit format version. A v3 descriptor creates
a v3 kit, including when you use a Docker template image as its base. See
[Version compatibility](/manuals/ai/sandboxes/customize/_index.md#version-compatibility).
### Package an existing agent image
This example packages Docker's OpenCode image as a v3 workload. The image
already has an agent installed, so you only need to choose its launch command
and describe what it needs to run. You can run the resulting kit directly or
include it in a set with additional tools.
Create a directory with these two files:
```text
opencode-workload/
├── opencode-workload.yaml
└── opencode-workload.dockerfile
```
The Dockerfile selects the base image and tells Docker Sandboxes to run
OpenCode as the `agent` user:
```dockerfile {title="opencode-workload/opencode-workload.dockerfile"}
FROM docker/sandbox-templates:opencode
USER agent
ENTRYPOINT ["opencode"]
CMD []
```
The template supplies OpenCode, Python, uv, and the `agent` user. Create a
YAML descriptor to identify this as a workload and declare what OpenCode
needs: network access, an Anthropic API key, and instructions about its
environment:
```yaml {title="opencode-workload/opencode-workload.yaml"}
# syntax=docker/sandbox-kit:3
schemaVersion: "3"
kind: workload
capabilities:
- type: com.docker.sandbox/sbx@1
- type: com.docker.sandbox/network-policy@1
config:
runtime:
allow:
- api.anthropic.com
- opencode.ai
- models.dev
- registry.npmjs.org
- pypi.org
- files.pythonhosted.org
- type: com.docker.sandbox/credential@1
config:
service: anthropic
phase: runtime
apiKey:
name: ANTHROPIC_API_KEY
proxyManaged: true
inject:
- domain: api.anthropic.com
header: x-api-key
format: "%s"
- type: com.docker.sandbox/agent-context@1
config:
filename: AGENTS.md
content: |
OpenCode runs as the agent user. Python and uv are available.
Use the project's environment and dependency configuration.
```
The credential entry names the service and describes how to authenticate API
requests. Store the actual API key on your host.
`sbx` can build this directory when you create a sandbox. The result is a
container image with the kit's files and descriptor, which you can also
publish to a registry for others to use.
## Use your own Linux image
If you need a different operating system or set of packages, start from a
Linux image of your choice. You'll need to add the tools and user account
that Docker Sandboxes expects, then install your agent.
The following requirements help you prepare that image. For a step-by-step
example, follow [Build an agent workload](/manuals/ai/sandboxes/customize/author/build-an-agent.md).
### Base image requirements
Prepare your image with the following:
- Tools: Include `curl`, `git`, and trusted CA certificates for accessing
source repositories and making HTTPS requests.
- Shells: Provide executable `/bin/sh` and `/bin/bash` files for setup commands
and agent launch.
- User account: Create a non-root `agent` account with UID 1000 and home
directory `/home/agent`. Add the account to `/etc/passwd` and set `USER
agent` in the image.
- Launch command: Set `ENTRYPOINT` or `CMD` to the agent or shell you want the
sandbox to launch.
Docker Sandboxes uses the account's entry in `/etc/passwd` to determine
the user ID, group ID, and home directory when running commands and writing
files.
For a sandbox with a mounted workspace, `WORKDIR` doesn't choose the mount
location. For a sandbox without a mounted workspace, `sbx` uses the image's
absolute `WORKDIR` as the working directory. If the image doesn't specify a
usable absolute path, `sbx` falls back to `/home/agent/workspace`.
### Declare the agent launch contract
`sbx` keeps the sandbox running and starts the agent as a separate process,
using the image's launch command. The `com.docker.sandbox/sbx@1` capability
declares that your workload is prepared to run this way:
```yaml
capabilities:
- type: com.docker.sandbox/sbx@1
```
This capability takes no `config` and doesn't request extra permissions. It
lets the upstream conformance tests check that your image meets the shell and
user account requirements before you publish.
The host launches the agent through non-interactive Bash. To load persistent
environment settings at launch, set `BASH_ENV` to an absolute path and include
that file in the image. Bash reads this file instead of login profiles or
`.bashrc`.
For the complete contract, see the upstream
[`sbx@1` definition](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/capabilities/com.docker.sandbox/sbx@1.md).

View File

@@ -0,0 +1,419 @@
---
title: Build an agent workload
linkTitle: Build an agent workload
description: Build a schema v3 agent workload from your own Linux base image, prepare its sandbox environment, and configure credentials and agent instructions.
keywords: sandboxes, sbx, kits, agent, tutorial, claude, workload, build
weight: 20
aliases:
- /ai/sandboxes/customize/build-an-agent/
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
Build a kit that runs Claude Code on a Linux base image of your choice.
You'll install the agent, give it access to the Anthropic API, and add model
settings and instructions for your team. The result is a v3 workload kit you
can run locally or publish for others to use. You can follow the same steps
for other agents or your organization's own base images.
This tutorial prepares the whole environment, from system packages to the
command that starts the agent. If you already have a suitable agent image,
see [Package an existing agent image](/manuals/ai/sandboxes/customize/author/base-images.md#package-an-existing-agent-image).
To combine a published agent kit with tools, [author a kit set](/manuals/ai/sandboxes/customize/author/kit-sets.md).
If you're starting with kits, read [Author kits](/manuals/ai/sandboxes/customize/author/_index.md)
for an introduction to the files you'll create. Field definitions are in the
[upstream v3 specification](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/SPEC-v3.md).
## Prepare the kit directory
You need `sbx`, Docker with Buildx, and an Anthropic
API key. Create a directory beside the project you want the agent to work on:
```console
$ mkdir claude-team
```
The completed directory contains three files:
```text
claude-team/
├── claude-team.yaml
├── claude-team.dockerfile
└── context.md
```
The Dockerfile installs the agent and sets the command to run it. The YAML
file, called the descriptor, tells Docker Sandboxes what the agent needs to
run, such as network access and credentials. The Markdown file contains
instructions for the agent. Give the YAML file and Dockerfile the same name
before the extension so the build can find both files.
This kit uses v3 and is independent of the built-in `claude` kit, which uses
v2. Any mixins you add must also use v3. To customize the built-in kit, see
[Kits v2](/manuals/ai/sandboxes/customize/kits-v2.md).
## Use your own base image
This tutorial starts from Red Hat Universal Base Image (UBI) 9. The following
steps add the tools, user account, and certificates the sandbox needs. You can
use another Linux image, including one maintained by your organization.
Adjust the package and account commands for that image, following the same
[base image requirements](/manuals/ai/sandboxes/customize/author/base-images.md#base-image-requirements).
### Install the system packages
Create `claude-team/claude-team.dockerfile` with the base image and packages:
```dockerfile {title="claude-team/claude-team.dockerfile"}
FROM registry.access.redhat.com/ubi9/ubi:9.8
USER root
RUN dnf install -y bash ca-certificates curl-minimal git shadow-utils tar gzip \
&& dnf clean all
```
Bash runs shell commands, Git accesses source repositories, and curl uses
the CA certificates to make HTTPS requests. The remaining packages create
the agent's user account and unpack its installer. Add any compilers,
libraries, or other tools your projects need here.
### Create the agent account
The sandbox needs a non-root `agent` user with UID 1000 and home directory
`/home/agent`. Append the following to the Dockerfile to create that account
and writable directories for its workspace, configuration, and state:
```dockerfile {title="Append to claude-team/claude-team.dockerfile"}
RUN groupadd --gid 1000 agent \
&& useradd --uid 1000 --gid 1000 --create-home --shell /bin/bash agent \
&& mkdir -p /home/agent/workspace /home/agent/.local/bin \
/home/agent/.local/share /home/agent/.local/state \
/home/agent/.config/claude-team /home/agent/.docker/sandbox/locks \
&& chown -R agent:agent /home/agent
```
Creating these directories during the build makes the agent their owner
before Docker Sandboxes mounts the workspace and storage. The agent runs
without `sudo` in this example, so install system packages in the Dockerfile.
Use install hooks for setup that depends on an individual sandbox.
### Prepare certificate trust
HTTPS requests from the sandbox go through a proxy. Docker Sandboxes adds
the proxy's certificate authority (CA) to the sandbox's trusted certificates
when it starts. UBI keeps its certificates at a different path from the one
`sbx` uses. Copy them to the expected path, then tell tools to use that file
so they trust both public certificates and the proxy:
```dockerfile {title="Append to claude-team/claude-team.dockerfile"}
RUN update-ca-trust \
&& mkdir -p /usr/local/share/ca-certificates /etc/ssl/certs \
&& cp /etc/pki/tls/certs/ca-bundle.crt /etc/ssl/certs/ca-certificates.crt
ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \
CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \
REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \
NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
```
If you add corporate CA certificates during the build, add them to
`/etc/pki/ca-trust/source/anchors/` before running `update-ca-trust` and
copying the bundle. For another distribution, use its certificate management
command and source bundle path.
### Prepare the shell environment
An environment variable exported in one shell isn't automatically available
in another. Give the agent a file where it can save variables for later
shell sessions. The following lines create that file and arrange for login,
interactive, and non-interactive Bash shells to read it:
```dockerfile {title="Append to claude-team/claude-team.dockerfile"}
RUN touch /etc/sandbox-persistent.sh \
&& chown agent:agent /etc/sandbox-persistent.sh \
&& chmod 0644 /etc/sandbox-persistent.sh \
&& printf '%s\n' '. /etc/sandbox-persistent.sh' \
> /etc/profile.d/sandbox-persistent.sh \
&& printf '%s\n' '. /etc/sandbox-persistent.sh' >> /home/agent/.bashrc
ENV HOME=/home/agent \
PATH="/home/agent/.local/bin:${PATH}" \
BASH_ENV=/etc/sandbox-persistent.sh
```
`BASH_ENV` tells non-interactive Bash to read the file. You'll include
instructions for the agent to use it later in the tutorial.
## Build the agent into the image
Append the following to the Dockerfile to install Claude Code as `agent` and
set its launch command:
```dockerfile {title="Append to claude-team/claude-team.dockerfile"}
USER agent
ENV CLAUDE_ENV_FILE=/etc/sandbox-persistent.sh \
IS_SANDBOX=1
ARG CLAUDE_VERSION
RUN curl -fsSL https://claude.ai/install.sh -o /tmp/install-claude.sh \
&& bash /tmp/install-claude.sh "${CLAUDE_VERSION}" \
&& rm /tmp/install-claude.sh
WORKDIR /home/agent/workspace
ENTRYPOINT ["claude", "--settings", "/home/agent/.config/claude-team/settings.json"]
CMD []
```
Installing as `agent` puts Claude Code under `/home/agent/`, where that user
can access it. `CLAUDE_ENV_FILE` points Claude Code to the file you prepared
for persistent environment variables. You'll set `CLAUDE_VERSION` in the
descriptor in the next step.
The Dockerfile also sets the user, working directory, environment variables,
and launch command. `CMD []` clears any arguments inherited from the base
image. The `--settings` option loads the model you choose for each sandbox.
You'll create its settings file in [Write the model settings](#write-the-model-settings).
Each sandbox created from this image gets the same Claude Code binary.
The next steps configure how Docker Sandboxes runs it. Any mixins you add
must use v3 and contain tools compatible with this workload's operating
system and architecture.
## Describe the workload and its inputs
Create `claude-team/claude-team.yaml`. Start by identifying the workload and
declaring two arguments:
- `version` selects the Claude Code version to install during the image build.
- `model` selects the model to use when creating a sandbox.
```yaml {title="claude-team/claude-team.yaml"}
# syntax=docker/sandbox-kit:3
schemaVersion: "3"
kind: workload
displayName: Team Claude Code
description: Claude Code with team defaults and API-key authentication
args:
version:
default: "2.1.278"
pattern: '^[0-9]+\.[0-9]+\.[0-9]+$'
buildArg: CLAUDE_VERSION
model:
default: sonnet
enum: [sonnet, opus, haiku]
provides: ["claude@${{ kit.args.version }}"]
```
`version` sets the Dockerfile's `CLAUDE_VERSION` build argument. The build
checks the version against `pattern`, then includes it in `provides` so
other kits can check which Claude Code version is installed.
`model` lets you choose a model when creating each sandbox. The choice goes
into a settings file, so it doesn't change the installed agent.
## Allow access to the API
With the agent installed, the next step is to give it access to the Anthropic
API. Add a `capabilities` list at the top level of `claude-team.yaml`, after
`provides`:
```yaml {title="Add to claude-team/claude-team.yaml"}
capabilities:
- type: com.docker.sandbox/sbx@1
- type: com.docker.sandbox/network-policy@1
config:
runtime:
allow:
- api.anthropic.com:443
```
The network rule permits HTTPS requests to the Anthropic API while the
sandbox runs. Downloading Claude Code in the Dockerfile uses the builder's
network, so it doesn't need a rule here.
The `sbx@1` entry declares that the workload is prepared for `sbx` to manage
the agent's launch. See [Declare the agent launch contract](/manuals/ai/sandboxes/customize/author/base-images.md#declare-the-agent-launch-contract)
for the shells and user account that this requires.
## Declare the credential
Claude Code can reach the API, but it also needs to authenticate. Add a
credential capability to tell Docker Sandboxes which key to use and how to
include it in requests. You'll store the key on your host, outside the kit.
Append this entry to the same `capabilities` list:
```yaml {title="Append under capabilities"}
- type: com.docker.sandbox/credential@1
description: Anthropic API access
config:
service: anthropic
phase: runtime
apiKey:
name: ANTHROPIC_API_KEY
proxyManaged: true
inject:
- domain: api.anthropic.com
header: x-api-key
format: "%s"
```
The sandbox receives a placeholder in `ANTHROPIC_API_KEY`. When Claude Code
makes a request to `api.anthropic.com`, the host proxy inserts the real API
key into the `x-api-key` header. The key stays on the host. You'll supply
its value and approve its use when launching the kit.
## Write the model settings
The Dockerfile's launch command reads
`/home/agent/.config/claude-team/settings.json`. Use the lifecycle capability
to create that file with the model chosen for the sandbox.
Append this entry to `capabilities`:
```yaml {title="Append under capabilities"}
- type: com.docker.sandbox/lifecycle@1
config:
files:
- path: /home/agent/.config/claude-team/settings.json
content: |
{"model": "${{ kit.args.model }}"}
mode: "0644"
```
Docker Sandboxes fills in the chosen `model` and writes the file before
Claude Code starts. Creating the file at this point lets each sandbox use a
different model with the same image.
Docker Sandboxes writes these files as UID 1000 after running install hooks.
Use an absolute path the agent can write to, as in this example. Shell
variables such as `$HOME` aren't expanded in the path.
## Add agent instructions
The kit can also give Claude Code instructions about the environment. Save the
following Markdown alongside the descriptor and Dockerfile:
```markdown {title="claude-team/context.md"}
## Team workflow
Read the project's README before changing code. Run the project's checks
before reporting a task complete, and report any checks you couldn't run.
Claude Code is installed in this sandbox. Its additional settings are at
`/home/agent/.config/claude-team/settings.json`.
Use `/etc/sandbox-persistent.sh` for environment exports needed by later
Bash commands. Keep shell completion scripts out of that file because
non-interactive commands also source it.
```
Append an agent-context entry to `capabilities` to include these instructions:
```yaml {title="Append under capabilities"}
- type: com.docker.sandbox/agent-context@1
config:
filename: CLAUDE.md
contentFile: ./context.md
```
The build includes `context.md` in the image. When the sandbox runs, `sbx`
writes a `CLAUDE.md` file in the parent directory of the mounted workspace.
That file points Claude Code to your `context.md`. A `CLAUDE.md` in your
project stays in place.
Use `contentFile`, as in this example, to keep longer instructions in a
separate Markdown file. You can also write instructions directly in the
workload's descriptor using `content` instead of `contentFile`.
For how instructions from multiple kits work together, see
[Runtime access and instructions](/manuals/ai/sandboxes/customize/use-kits.md#runtime-access-and-instructions).
## Store the key and run
Store your Anthropic API key on the host:
```console
$ sbx secret set anthropic
```
From the directory containing `claude-team`, launch the kit:
```console
$ sbx run --name claude-team ./claude-team
```
`sbx` builds the kit, mounts your current directory as the workspace, and
launches Claude Code. To work on another project, append its path to the command.
When prompted, approve the kit's request to use your stored key, then follow
Claude Code's first-run prompts. The agent needs both the stored key and your
approval to use it. See
[Credential bindings](/manuals/ai/sandboxes/configuration/credentials.md#credential-bindings).
Choose a different model when creating a sandbox:
```console
$ sbx run --name claude-team-opus ./claude-team \
--kit-arg claude-team.model=opus
```
The `claude-team` prefix matches the local kit directory's name. Docker
Sandboxes checks that `opus` is one of the choices in `enum`, then writes it
to the settings file before Claude Code starts.
## Iterate and publish
Edit the descriptor, Dockerfile, or context file and create another sandbox
with a different name to test the changes:
```console
$ sbx run --name claude-team-test-2 ./claude-team
```
Restarting an existing sandbox won't pick up your edits. To try another
agent version locally, update `args.version.default` in the descriptor and
create another sandbox.
`sbx` reuses the local build when the source files and kit arguments are
unchanged. Changing either can trigger a build, even for arguments such as
`model` that only affect sandbox setup. BuildKit can still reuse unchanged
image layers.
### Publish the workload
When the kit is ready to share, sign in to Docker Hub, then build and push it
with Docker Buildx. Replace `<NAMESPACE>` with a Docker Hub namespace you can
push to:
```console
$ docker login
$ docker buildx build ./claude-team \
--file ./claude-team/claude-team.yaml \
--tag docker.io/<NAMESPACE>/claude-team:1.0.0 \
--push
```
The `--file` option tells Buildx to read the descriptor. Its `syntax` line
selects the kit frontend, which reads the companion Dockerfile and includes
the descriptor in the published image. To publish a different agent version,
add `--build-arg version=<CLAUDE_VERSION>`. This flag uses the kit argument
name, `version`, which the descriptor maps to the Dockerfile's `CLAUDE_VERSION`.
Run the published kit by its image reference:
```console
$ sbx run --name claude-team-shared docker.io/<NAMESPACE>/claude-team:1.0.0
```
For multi-platform images and distribution details, see
[Build and distribute kits](/manuals/ai/sandboxes/customize/author/distribute.md).
To learn how to package a tool separately from its workload, see
[Build a tool mixin](/manuals/ai/sandboxes/customize/author/tool-mixins.md).
That tutorial also packages Claude Code, so use its mixin with a shell workload.
Combining it with this workload would give two kits that provide `claude`,
which Docker Sandboxes rejects.
You can also include the published workload in a
[kit set](/manuals/ai/sandboxes/customize/author/kit-sets.md) and add settings
and instructions there. The workload still defines how to prepare the base
image, install the agent, and launch it.

View File

@@ -0,0 +1,117 @@
---
title: Build and distribute kits
description: Build and publish v3 kit images, share source through Git, and sign images for distribution.
keywords: sandboxes, sbx, kits, v3, workloads, mixins
weight: 40
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
Once you've [authored a kit](/manuals/ai/sandboxes/customize/author/_index.md),
share it by publishing an image to a registry or its source to Git.
Publishing an image saves others from building the kit themselves. Sharing
the source lets them build it when they create a sandbox. Either way, `sbx`
reads the kit's descriptor to configure the sandbox.
This page shows how to publish and sign v3 kits. To run a kit someone else has
shared, see [Use kits](/manuals/ai/sandboxes/customize/use-kits.md).
## Publish an image
Use Docker Buildx to build and publish your kit as an OCI image. Pass the YAML
descriptor with `-f` and the source directory as the build context:
```console
$ docker login
$ docker buildx build ./my-kit -f ./my-kit/my-kit.yaml \
-t docker.io/<NAMESPACE>/my-kit:1.0.0 --push
```
Replace `<NAMESPACE>` with a Docker Hub namespace you can push to. Buildx
uses your `docker login` credentials to push the image.
Push the image before running it by its registry reference. `sbx` pulls the
published image. It can't use images stored only in your host's Docker image
store. During development, you can pass a local source directory to `sbx`
instead. For private images, configure
[registry credentials](/manuals/ai/sandboxes/configuration/credentials.md#registry-credentials)
for your sandbox.
Use Buildx for v3 kits. The `sbx kit pack`, `push`, and `pull` commands are
for v1 and v2 kits.
### Support both Linux architectures
To share the kit with people who use different machines, build for both
supported Linux architectures:
```console
$ docker buildx build ./my-kit -f ./my-kit/my-kit.yaml \
--platform linux/amd64,linux/arm64 \
-t docker.io/<NAMESPACE>/my-kit:1.0.0 --push
```
## Publish a kit set
Publish a set with the same Buildx command, passing its YAML descriptor with
`-f`. The build fetches the published images listed in `kits:`, checks whether
their declarations are compatible, and combines their files and settings into
one image. See
[Compose a kit set](/manuals/ai/sandboxes/customize/author/kit-sets.md) for a complete example.
The result is a workload or mixin image that people can use directly. It
records each component's manifest digest, which identifies the exact image
used in the build. Updating a component's tag doesn't change a published
set. To share the update, rebuild the set and publish another version.
If you want rebuilds to use the same component images, add a `digest` beside
each `ref` in `kits:`.
## Share source through Git
Commit the kit's source directory to a Git repository, then share a reference
in this format. Use `dir` to select the kit directory and `ref` to select the
commit:
```text
git+https://github.com/<ORG>/<REPOSITORY>.git#ref=<COMMIT>&dir=my-kit
```
When someone creates a sandbox from that reference, `sbx` builds the kit
from the selected commit.
## Sign and verify kits
Sign your published image so others can verify who signed it:
```console
$ sbx kit sign docker.io/<NAMESPACE>/my-kit:1.0.0
$ sbx kit verify docker.io/<NAMESPACE>/my-kit:1.0.0 \
--certificate-identity <SIGNER_IDENTITY> \
--certificate-oidc-issuer <ISSUER_URL>
```
These commands use Sigstore signatures, which are compatible with Cosign.
The example signs without a key, so verification needs the signer's certificate
identity and OpenID Connect issuer. To sign with a key instead, pass
`--key cosign.key` to `sign` and `--key cosign.pub` to `verify`.
To require a valid signature before using a kit, see
[Verify kit signatures](/manuals/ai/sandboxes/customize/use-kits.md#verify-kit-signatures).
V3 kits shared as source directories or Git references can't be signed.
If you need signatures, publish and sign an OCI image.
## Published format
A published kit image contains both the kit's content and its descriptor.
Docker image tools can inspect and distribute it, and Docker Sandboxes reads
the descriptor when creating the sandbox.
You can also inspect the descriptor inside the sandbox, under
`/usr/share/sandbox/kit/<stem>/`.
For the image annotations and file layout, see
[Published image format](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/SPEC-v3.md#10-the-oci-layout).
Use `sbx` to run a workload with the settings and hooks declared in its kit
descriptor. Running it with `docker run` uses only the image configuration,
so those settings and hooks aren't applied.

View File

@@ -0,0 +1,244 @@
---
title: Compose a kit set
description: Combine published workloads and mixins into a kit set, configure shared settings and arguments, and publish it for your team.
keywords: sandboxes, sbx, kits, v3, sets, composition, workloads, mixins
weight: 5
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
A kit set gives your team one kit to run, with the tools and versions you've
chosen. You list the kits to include, add any settings the combined environment
needs, and publish the result. A set can also run setup commands, supply agent
instructions, and offer choices such as which model to use.
Use a set when you want to share a combination of kits and manage its
settings in one place. The components can be kits your team publishes or
kits from other publishers. You need Docker Buildx and a registry namespace
you can push to when publishing the set.
## Choose the components
A set can contain one workload and any number of mixins. Together, they
provide a complete sandbox environment. You can also make a set of only
mixins, to share tools and settings that users add to a workload with `--kit`.
Choose v3 kits that work together. For example, you could combine an agent
workload with a linter mixin and a mixin that adds your team's configuration.
To package a tool of your own, see
[Build a tool mixin](/manuals/ai/sandboxes/customize/author/tool-mixins.md).
You can try a workload and mixins together with `sbx run` and `--kit`
before composing a set. See
[Add mixins](/manuals/ai/sandboxes/customize/use-kits.md#add-mixins).
If one kit depends on another, Docker Sandboxes applies the dependency first.
Changing the order of `--kit` flags or entries in a set doesn't change that
order.
## Write the set descriptor
A set descriptor uses `kind: set` and lists its components under `kits:`.
For example, this descriptor combines Docker's Codex workload with a
mixin:
```yaml {title="codex-tools/codex-tools.yaml"}
# syntax=docker/sandbox-kit:3
schemaVersion: "3"
kind: set
displayName: Codex with tools
version: "1.0.0"
kits:
- ref: docker.io/docker/sbx-kit-codex:0.155.1
- ref: <MIXIN_REFERENCE>
```
Replace `<MIXIN_REFERENCE>` with a published v3 mixin's full image reference.
Choose a different workload reference to use another agent or environment.
The components supply their own network rules and credential requests, so
the set doesn't need to repeat them.
The set gets its software and files from the `kits:` list. It doesn't have a
Dockerfile, a `dockerfile:` field, or a `build:` block. To include more software
or static files, package them in a workload or mixin, publish that kit, and
add it to the list.
Every `ref` must be a published registry reference. Local paths and Git URLs
aren't accepted as components, even when building the set from local source.
## Add runtime access to the set
You can give the combined environment access to services beyond those its
components request. For example, to let the agent download packages from an
internal registry, add this network capability to the set's descriptor:
```yaml
capabilities:
- type: com.docker.sandbox/network-policy@1
config:
runtime:
allow:
- packages.company.example:443
```
Replace the example domain with your registry's host. The rule permits access
as long as the sandbox's policy allows it. If the registry also requires
authentication, add a credential request. See
[Services declared by kits](/manuals/ai/sandboxes/configuration/credentials.md#services-declared-by-kits).
Keep access required by a tool in that tool's kit, so its access rules follow
it when used with another workload. Put settings shared by the combined
environment on the set, such as access to your team's package registry.
You can also add setup commands, generate configuration files, provide
instructions, and define arguments on the set.
## Build and publish the set
Before publishing, [check for file conflicts](#check-for-file-conflicts)
between the components.
Build and publish a set by passing its descriptor to Docker Buildx.
For a descriptor at `codex-tools/codex-tools.yaml`:
```console
$ docker login
$ docker buildx build ./codex-tools -f ./codex-tools/codex-tools.yaml \
-t docker.io/<NAMESPACE>/codex-tools:1.0.0 --push
```
Replace the paths with your set's source directory and descriptor, and
`<NAMESPACE>` with a Docker Hub namespace you can push to.
The build pulls the listed kits, checks their declared requirements, and
combines their files and settings. It fails if a dependency is missing, two
kits declare the same feature in `provides`, or more than one kit is a workload.
A set that includes a workload publishes as `kind: workload`.
A set containing only mixins publishes as `kind: mixin`.
Your team can use the result like any other workload or mixin. The published
kit records each component's exact image digest in `kits:`. Only the source
descriptor uses `kind: set`.
## Run the set
Run a published set that contains a workload by passing its image reference
to `sbx run`, as you would for an individual workload:
```console
$ sbx run <SET_REFERENCE> --name my-project
```
The sandbox includes the workload and all the mixins packaged in the set.
You don't need to list those mixins separately with `--kit`.
For a set containing only mixins, add it to a workload with `--kit`:
```console
$ sbx run <WORKLOAD_REFERENCE> --kit <SET_REFERENCE> --name my-project
```
Authentication depends on the kits in the set. Check their documentation for
required credentials, store those credentials on the host, and approve access
when prompted. See
[Credential configuration](/manuals/ai/sandboxes/configuration/credentials.md)
for authentication options and preparing unattended runs.
To control the agent's base image or launch command, see
[Build an agent workload](/manuals/ai/sandboxes/customize/author/build-an-agent.md).
## Configure component arguments
You can let your team change a setting without changing which kits are
included. For example, they might need to choose whether a linter checks or
fixes files while using the linter version you've selected.
A set controls which component arguments users can change. You can fix an
argument's value when publishing the set, or expose it as an argument on the
set so users can choose a value when creating a sandbox.
After changing the argument definitions in the descriptor, rebuild and publish
the set.
### Fix an argument's value
Suppose you have a linter kit with a `mode` argument that users normally set
when creating a sandbox. Add it to the set with a fixed value of `check`:
```yaml
kits:
- ref: docker.io/my-org/linter-kit:1.0.0
args:
mode: check
```
This excerpt fixes the linter's `mode` to `check` when you publish the set.
Use your linter's image reference and argument name. Other components in the
set can have their own argument values.
### Let users choose a value
To let users choose the mode when creating a sandbox, define an argument on
the set and pass its value to the linter:
```yaml
args:
lint_mode:
default: check
enum: [check, fix]
kits:
- ref: docker.io/my-org/linter-kit:1.0.0
args:
mode: ${{ kit.args.lint_mode }}
```
This example assumes the linter defines `mode` with the same default and
allowed values. The set must preserve those constraints. Users can then pass
`--kit-arg lint_mode=fix` when running the published set. They can change only
arguments defined on the set, not other arguments on its components.
See [Set merge rules](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/SPEC-v3.md#95-merging-a-set)
for the argument rules.
### Change a build argument
Arguments used to build a component, such as the tool version to install,
are fixed in its published image. To change one, rebuild and publish the
component, then update its reference in the set.
## Review and update the composition
### Check for file conflicts
Before sharing a set, check that its components work together. Give each
component's files separate paths: if two components include the same path,
the file from the later image layer replaces the earlier one. The build
orders layers by kit dependencies, so reordering `kits:` doesn't choose
which file wins. Inspect the built image for unintended replacements.
When you combine kits with `--kit` instead, Docker Sandboxes rejects files
that collide when it creates the sandbox.
### Check combined settings
The set includes each component's network rules and agent instructions.
Lifecycle hooks run in dependency order, but startup hooks run alongside
the agent. The agent might start before those hooks finish.
Use install hooks for setup that must finish during sandbox creation. For
setup that must finish before every agent launch, use the workload's
entrypoint.
The workload chooses the instruction filename. If you add instructions with
an `agent-context` capability on the set, leave out the filename.
### Update a component
Change the component's reference, rebuild the set, and publish another
version. Create another sandbox to try the updated set. Existing sandboxes
keep the kit configuration they were created with.
A published set keeps the files and settings it was built with, even if a
component's tag later points to a different image. To use those same images
when rebuilding, add a `digest` beside each `ref` in the source.
For signing and multi-platform builds, see
[Build and distribute kits](/manuals/ai/sandboxes/customize/author/distribute.md).

View File

@@ -0,0 +1,147 @@
---
title: Kit authoring patterns
linkTitle: Authoring patterns
description: Build reusable kits that carry the access their tools need, prepare certificates and storage during setup, and offer useful options to your team.
keywords: sandboxes, sbx, kits, patterns, mixins, lifecycle, certificates
weight: 35
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
When you package a tool as a kit, aim to make it work wherever you add it.
These patterns show how to include the access it needs, prepare it for each
sandbox, and give your team useful options without asking them to assemble
the environment themselves.
## Keep runtime access with the tool
Keep a tool's network rules and credential request in the mixin that installs
it. For example, a Claude Code mixin can package the executable, allow access
to the Anthropic API, and request the API key. Those requirements follow the
tool when you add it to another workload.
The user still needs to supply the credential and approve access. See
[Build a tool mixin](/manuals/ai/sandboxes/customize/author/tool-mixins.md)
for a complete example.
## Leave build tools out of the mixin
Build a tool in one Dockerfile stage, then copy the executable into an empty
stage. Each sandbox gets the tool without also getting its compiler and
source code:
```yaml {title="gojq/gojq.yaml"}
# syntax=docker/sandbox-kit:3
schemaVersion: "3"
kind: mixin
provides: ["gojq@0.12.17"]
build: |
FROM golang:1.25 AS build
RUN CGO_ENABLED=0 go install github.com/itchyny/gojq/cmd/gojq@v0.12.17
FROM scratch
COPY --from=build /go/bin/gojq /usr/local/bin/gojq
```
`CGO_ENABLED=0` builds gojq without a dependency on the workload's C libraries.
`FROM scratch` starts the final stage empty, so it contains only the binary
you copy. The `provides` entry tells other kits which gojq version this mixin
installs.
## Run setup after combining kits
Some setup needs tools or files from the workload. Package what you can in the
mixin, then use an install hook to finish setup once all the kits' files are
in place. For example, bring an internal certificate authority (CA) certificate
in the mixin and register it in the workload's trust store:
```yaml {title="internal-ca/internal-ca.yaml"}
# syntax=docker/sandbox-kit:3
schemaVersion: "3"
kind: mixin
build: |
FROM scratch
COPY internal-ca.crt /usr/local/share/ca-certificates/team-internal-ca.crt
capabilities:
- type: com.docker.sandbox/lifecycle@1
config:
install:
- command: update-ca-certificates
user: "0"
```
Save your PEM-encoded CA as `internal-ca.crt` beside the descriptor and use the
mixin with a workload that supplies `update-ca-certificates`, such as Docker's
shell kit. The hook updates the workload's trust store as root, after all kit
files are present and before the agent launches.
```console
$ sbx run docker.io/docker/sbx-kit-shell:1.0.0 --kit ./internal-ca
```
## Seed storage after it is mounted
Mounting storage at a path hides any files the image already has there. To
give a tool some initial data, keep that data elsewhere in the image and
copy it to the mounted directory in an install hook. Check whether the
destination file exists first so you preserve any changes the user has made:
```yaml {title="tool-state/tool-state.yaml"}
# syntax=docker/sandbox-kit:3
schemaVersion: "3"
kind: mixin
build: |
FROM scratch
COPY defaults.json /usr/share/company-cli/defaults.json
capabilities:
- type: com.docker.sandbox/volume@1
config:
path: /home/agent/.company-cli
size: 1g
- type: com.docker.sandbox/lifecycle@1
config:
install:
- command: |
set -eu
chown 1000:1000 /home/agent/.company-cli
if [ ! -e /home/agent/.company-cli/config.json ]; then
install -o 1000 -g 1000 -m 0644 /usr/share/company-cli/defaults.json /home/agent/.company-cli/config.json
fi
user: "0"
```
Save your tool's initial configuration as `defaults.json` beside the descriptor.
Use a workload with `chown` and `install`, such as Docker's shell kit. The hook
makes the directory and configuration file writable by the agent. If the
volume already has a configuration file, the hook leaves its contents intact.
### Configure the volume
The volume keeps the tool's data across sandbox restarts. A replacement
sandbox gets its own volume. This example allocates `1g` of space. If you
omit `size`, `sbx` allocates `512m`.
Use an install hook to set ownership and permissions for persistent volumes,
as this example does. The volume capability's `mode` setting applies only
to tmpfs mounts. See the upstream
[volume definition](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/capabilities/com.docker.sandbox/volume@1.md)
for storage options.
To copy initial files into a mounted workspace instead, use
`WORKSPACE_DIR` as the destination and
[declare it in the hook's environment](/manuals/ai/sandboxes/customize/author/_index.md#choose-when-setup-runs).
## Publish fixed components with configurable options
Choose compatible agent and tool versions for your team, then publish them
as a set. Expose arguments for settings users can change without replacing
those components, such as a model or linter mode. Keep version choices fixed
in the published images.
For the argument syntax and constraints, see
[Configure component arguments](/manuals/ai/sandboxes/customize/author/kit-sets.md#configure-component-arguments).

View File

@@ -0,0 +1,210 @@
---
title: Build a tool mixin
description: Package Claude Code as a reusable mixin with API access and credentials, test it in a shell workload, and publish it.
keywords: sandboxes, sbx, kits, mixins, tools, credentials, build
weight: 30
aliases:
- /ai/sandboxes/customize/kit-examples/
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
A tool mixin adds an executable and the settings it needs to an existing
workload. This tutorial packages Claude Code as a mixin, adds it to a shell
workload, and tests a request to the Anthropic API. The sandbox starts a shell,
and you choose when to run Claude Code.
The example adapts the upstream
[Claude Code mixin](https://github.com/docker/sandbox-kit-spec/tree/main/examples/claude-mixin)
to use API-key authentication. The upstream example also includes OAuth,
session storage, and MCP configuration.
You need `sbx`, Docker with Buildx, and an Anthropic API key. To publish the
mixin, you also need a registry namespace you can push to. Use a v3 workload
with this mixin. Built-in shortcuts such as `shell` and `claude` use v2. See
[Version compatibility](/manuals/ai/sandboxes/customize/_index.md#version-compatibility).
## Package the executable
Create a `claude-mixin` directory with a Dockerfile and YAML descriptor:
```text
claude-mixin/
├── claude-mixin.dockerfile
└── claude-mixin.yaml
```
The Dockerfile downloads the native Claude Code executable in a build stage,
then copies it into an empty image:
```dockerfile {title="claude-mixin/claude-mixin.dockerfile"}
FROM debian:trixie-slim AS build
ARG CLAUDE_VERSION
ARG TARGETARCH
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates
RUN case "$TARGETARCH" in \
amd64) platform=linux-x64 ;; \
arm64) platform=linux-arm64 ;; \
*) echo "unsupported TARGETARCH: $TARGETARCH" >&2; exit 1 ;; \
esac \
&& mkdir -p /out/usr/local/bin \
&& curl -fsSL "https://downloads.claude.ai/claude-code-releases/${CLAUDE_VERSION}/${platform}/claude" \
-o /out/usr/local/bin/claude \
&& chmod 0755 /out/usr/local/bin/claude
FROM scratch
COPY --from=build /out/ /
```
BuildKit supplies `TARGETARCH` to select the download for the image's Linux
architecture. You'll set `CLAUDE_VERSION` in the descriptor. The final stage
contains only the executable, so the mixin doesn't add Debian or the download
tools to the workload.
Docker Sandboxes adds the mixin's files to the workload's environment. The
workload keeps its own startup command, user, and working directory. This
example uses Docker's shell workload. When choosing another workload, check
that it supplies the Linux libraries and shell your tool needs.
A mixin includes only the files its Dockerfile adds or changes. If you use
a larger base image instead of `scratch`, the mixin won't include that
image's unchanged files.
## Declare runtime access
Create the descriptor with the version to install and the API access Claude
Code needs:
```yaml {title="claude-mixin/claude-mixin.yaml"}
# syntax=docker/sandbox-kit:3
schemaVersion: "3"
kind: mixin
displayName: Claude Code mixin
args:
version:
default: "2.1.278"
pattern: '^[0-9]+\.[0-9]+\.[0-9]+$'
buildArg: CLAUDE_VERSION
provides: ["claude@${{ kit.args.version }}"]
capabilities:
- type: com.docker.sandbox/network-policy@1
config:
runtime:
allow:
- api.anthropic.com:443
- type: com.docker.sandbox/credential@1
config:
service: anthropic
phase: runtime
apiKey:
name: ANTHROPIC_API_KEY
proxyManaged: true
inject:
- domain: api.anthropic.com
header: x-api-key
format: "%s"
```
The `version` argument supplies `CLAUDE_VERSION` to the Dockerfile. The
`provides` entry identifies the installed Claude Code version so other kits
can check their requirements.
The network rule permits HTTPS requests to the Anthropic API. The credential
entry requests the key stored under `anthropic` on your host. Inside the
sandbox, `ANTHROPIC_API_KEY` contains a placeholder. The host proxy inserts
the real key into the `x-api-key` header when Claude Code calls the API.
Network access and credential access are separate: declaring a credential
doesn't allow connections to its service. This example pairs `phase: runtime`
with `runtime.allow`. The download in the Dockerfile uses the builder's
network and doesn't need a rule in the descriptor.
Keep the tool's access requirements in the mixin so they follow it when you
use it with another workload. Access shared by a project's tools, such as a
package registry, can go in a
[kit set](/manuals/ai/sandboxes/customize/author/kit-sets.md).
## Try the mixin
Store your Anthropic API key on the host:
```console
$ sbx secret set anthropic
```
From the directory containing `claude-mixin`, add the mixin to Docker's
published shell workload:
```console
$ sbx run docker.io/docker/sbx-kit-shell:1.0.0 --name claude-mixin-test \
--kit ./claude-mixin
```
Approve the mixin's credential request when prompted. `sbx` builds the mixin,
adds its executable to the workload, and opens a shell. The mixin doesn't
replace the workload's launch command.
Inside the sandbox, check that Claude Code runs:
```console
$ claude --version
```
Then send a request to check network access and authentication. This command
uses the Anthropic API and incurs usage charges:
```console
$ claude -p "Reply with the word hello."
```
A response confirms that Claude Code can reach the API and authenticate with
your stored key. If authentication fails, check that you stored the key and
approved the kit's request to use it. See
[Credential bindings](/manuals/ai/sandboxes/configuration/credentials.md#credential-bindings).
Network requests must also meet the sandbox's
[network policy](/manuals/ai/sandboxes/governance/concepts.md#precedence).
Run `claude` without arguments to start an interactive session. Follow its
first-run prompts. Exit Claude Code and the shell to return to your host.
After editing the kit's files, create a sandbox with a different name to test
your changes. Reopening an existing sandbox uses the kits it was created with.
## Publish the mixin
Build and publish the mixin so others can use it without building from source:
```console
$ docker login
$ docker buildx build ./claude-mixin -f ./claude-mixin/claude-mixin.yaml \
-t docker.io/<NAMESPACE>/claude-mixin:1.0.0 --push
```
Replace `<NAMESPACE>` with a Docker Hub namespace you can push to. Use the
published reference with `--kit` in place of `./claude-mixin`.
Choose a workload that doesn't already provide Claude Code. Two kits that
declare the same feature in `provides` conflict. To share the shell workload
and this mixin as one kit, see
[Compose a kit set](/manuals/ai/sandboxes/customize/author/kit-sets.md).
## More source examples
The [sandbox-kit-spec examples](https://github.com/docker/sandbox-kit-spec/tree/main/examples)
contain complete kits you can study and adapt:
- [Claude Code mixin](https://github.com/docker/sandbox-kit-spec/tree/main/examples/claude-mixin)
includes additional authentication options, session storage, and agent
instructions.
- [GitHub CLI](https://github.com/docker/sandbox-kit-spec/tree/main/examples/gh)
packages `gh` and its dependencies with network rules, credentials, and
agent instructions.
- [Message of the day](https://github.com/docker/sandbox-kit-spec/tree/main/examples/motd)
uses an inline Dockerfile and a build argument in a single-file mixin.
For examples of building a tool from source, running setup scripts, and other
reusable approaches, see
[Kit authoring patterns](/manuals/ai/sandboxes/customize/author/patterns.md).

View File

@@ -1,326 +0,0 @@
---
title: Build your own agent kit
linkTitle: Build an agent
description: Walk through building an agent kit for Amp, from base image choice to invocation.
keywords: sandboxes, sbx, kits, agent, tutorial, amp, ampcode
weight: 30
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
> [!NOTE]
> Kits are experimental. The kit file format, CLI commands, and experience
> for creating, loading, and managing kits are subject to change as the
> feature evolves. Share feedback and bug reports in the
> [docker/sbx-releases](https://github.com/docker/sbx-releases) repository.
This tutorial walks through building an agent kit for the
[Amp](https://ampcode.com/) coding agent. Each step explains the decision
behind a part of the spec, so you can apply the same reasoning to other agents.
For reference on every field, see the [Kit spec reference](kit-reference.md).
This tutorial focuses on the journey.
The finished kit is also published as a runnable sample at
[docker/sbx-kits-contrib](https://github.com/docker/sbx-kits-contrib/tree/main/amp) —
useful as a reference while you follow along.
## Choose a base image
An agent kit needs a container image that satisfies the
[base image requirements](kit-reference.md#base-image-requirements): non-root
`agent` user at UID 1000, passwordless sudo, `/home/agent/` home, and HTTP
proxy environment variable forwarding.
Rather than build an image from scratch, extend one of the published
sandbox templates. Three common starting points:
- `docker/sandbox-templates:shell`. Generic base with no pre-installed
agent.
- `docker/sandbox-templates:shell-docker`. Same, with Docker Engine inside
the sandbox.
- Agent-specific variants (`claude-code`, `codex`, etc.). Only useful if
you're extending that specific agent.
For Amp, pick `shell-docker`:
- Amp isn't pre-installed in any variant, so you need a generic base
(`shell`).
- Docker support is handy since coding agents often need to run containers.
- If you don't need Docker inside the sandbox, use the `shell` tag for a
lighter, non-privileged environment.
## Plan authentication
Amp authenticates with an API key in `AMP_API_KEY`. To keep the real key
out of the VM, you split the work in two:
- The kit's network section maps the API host to a service identifier
and tells the proxy which header to inject.
- You provide your key once on the host, via sbx's secret store. The
real value stays on the host; only a placeholder reaches the sandbox.
Inside the sandbox `AMP_API_KEY` is set to that placeholder. The proxy
substitutes the real key on outbound requests to the API host, so the
secret never enters the sandbox. A later section walks through the
specific command for storing the key.
## Write the sandbox block
The `sandbox:` block tells the sandbox how to launch Amp when the user
attaches.
```yaml {title="amp/spec.yaml"}
schemaVersion: "1"
kind: sandbox
name: amp
displayName: Amp
description: The frontier coding agent.
sandbox:
image: "docker/sandbox-templates:shell-docker"
aiFilename: AGENTS.md
entrypoint:
run: [amp, --dangerously-allow-all]
```
- `aiFilename: AGENTS.md` tells the sandbox to create `AGENTS.md` at launch
and append the [`agentContext`](#prime-amp-with-memory) block to it. Amp reads
this file for instructions.
- `entrypoint.run` runs `amp` in "YOLO-mode" when the sandbox starts. Adjust if
you want to pass different args on startup.
## Install Amp
Amp installs via a curl-to-bash script:
```yaml
commands:
install:
- command: "curl -fsSL https://ampcode.com/install.sh | bash"
user: "1000"
description: Install Amp
```
Note `user: "1000"`. That's the agent user. Install commands run as root
(UID 0) by default, and Amp's installer puts the binary in the user's home
directory. Running as root would land the binary in `/root/` where the
agent can't reach it.
## Allow network access
The network block does two things: it lists the hosts the sandbox can
reach (`allowedDomains`), and it wires the kit-side half of the auth flow
from [Plan authentication](#plan-authentication) with `serviceDomains` and
`serviceAuth`.
```yaml
network:
serviceDomains:
ampcode.com: amp
serviceAuth:
amp:
headerName: Authorization
valueFormat: "Bearer %s"
allowedDomains:
- "ampcode.com:443"
- "*.ampcode.com:443"
```
`allowedDomains` here covers the apex (`ampcode.com`) and the
install/CDN subdomains (`*.ampcode.com`). Treat it as a starting point;
Amp may reach other domains (model providers, analytics, updates) that
you'll discover by watching `sbx policy log` while testing.
Kits can also declare `deniedDomains` for hosts the sandbox should not
reach, such as telemetry endpoints. Deny rules take precedence over
allow rules and apply only to sandboxes that use the kit.
For the auth wiring, when the agent makes an outbound request to
`ampcode.com`, the proxy looks up the host in `serviceDomains` to find
the service id `amp`, then uses `serviceAuth.amp` to inject an
`Authorization: Bearer <key>` header. The `<key>` value comes from the
secret you'll register in
[Register your API key](#register-your-api-key), matched by host. The
service id (`amp`) is just a label that ties the two blocks together —
pick any name.
> [!IMPORTANT]
> Keep `serviceDomains` narrow. Mapping `*.ampcode.com` would push the
> proxy into TLS-intercepting mode for every subdomain — including the
> binary CDN the install script downloads from — which corrupts those
> downloads. List only the host that actually needs auth.
## Prime Amp with memory
The `agentContext` field appends markdown to `AGENTS.md` at sandbox creation.
Use it to tell Amp about the sandbox environment so it knows the
conventions when it starts.
```yaml
agentContext: |
## Sandbox environment
You are running inside a Docker sandbox. The workspace is mounted at
its absolute host path. `sudo` is passwordless; use it for package
installs. Docker is available inside the sandbox; containers you start
are isolated in the microVM.
```
Keep this short and sandbox-specific. For project instructions, put a
regular `AGENTS.md` in the workspace.
## The full spec
Putting it all together:
```yaml {title="amp/spec.yaml"}
schemaVersion: "1"
kind: sandbox
name: amp
displayName: Amp
description: The frontier coding agent.
sandbox:
image: "docker/sandbox-templates:shell-docker"
aiFilename: AGENTS.md
entrypoint:
run: [amp, --dangerously-allow-all]
network:
serviceDomains:
ampcode.com: amp
serviceAuth:
amp:
headerName: Authorization
valueFormat: "Bearer %s"
allowedDomains:
- "ampcode.com:443"
- "*.ampcode.com:443"
commands:
install:
- command: "curl -fsSL https://ampcode.com/install.sh | bash"
user: "1000"
description: Install Amp
agentContext: |
## Sandbox environment
You are running inside a Docker sandbox. The workspace is mounted at
its absolute host path. `sudo` is passwordless; use it for package
installs.
```
## Register your API key
Register your Amp API key on the host with `sbx secret set-custom`. The
value goes into the host secret store, and a placeholder is exposed
inside every sandbox you launch from this kit.
Amp validates `AMP_API_KEY`'s format at startup, so the placeholder needs
to look like a real Amp key. Pick a placeholder shape that matches Amp's
expected format:
```console
$ sbx secret set-custom \
--host ampcode.com \
--env AMP_API_KEY \
--placeholder "sgamp-{rand}" \
--value "$AMP_API_KEY"
```
`{rand}` expands to a random suffix at registration time. Inside the
sandbox `AMP_API_KEY` is set to that placeholder; Amp accepts it as a
syntactically valid key, and the proxy substitutes the real secret on
outbound requests to `ampcode.com`.
> [!TIP]
> `sbx secret set-custom` is only required because Amp validates the
> key's format. If your agent reads the env var without a local format
> check, you can declare `environment.proxyManaged: [AMP_API_KEY]` in
> the kit instead and skip this user-side step — the proxy uses a
> default sentinel value (`proxy-managed`) that the agent never sees
> rejected.
> [!NOTE]
> `sbx secret set-custom` is experimental and may change in future
> releases. This tutorial surfaces it because there's no other path to
> register a custom-format placeholder.
## Run it
Validate the spec:
```console
$ sbx kit validate ./amp/
```
Launch the sandbox by passing the kit directory in place of a built-in agent
name:
```console
$ sbx run ./amp/
```
The published copy of this kit also runs directly from the contrib
repository:
```console
$ sbx run "git+https://github.com/docker/sbx-kits-contrib.git#dir=amp"
```
## Iterate
As you use the kit, you'll likely hit missing domains or install quirks.
Two loops help:
- Watch the network policy log (`sbx policy log`) to catch blocked
requests, then add their domains to `allowedDomains`.
- Add domains to `deniedDomains` when the agent should stay blocked from
a host even if another policy permits it.
- Edit the spec and re-run `sbx run ./amp/` to pick up changes.
Remove the sandbox first (`sbx rm <name>`) for a clean start.
Flesh out the `agentContext` block as you refine how Amp should behave in the
sandbox.
## Publish
Once the kit works, share it by packaging as a ZIP, pushing to an OCI
registry, or committing to a Git repository. See
[Packaging and distribution](kits.md#packaging-and-distribution) for the
`sbx kit` subcommands.
## Adapt this to another agent
Most of the specifics here are Amp's. To port the pattern, work through
the same decisions for your agent:
- **Base image**: `shell-docker` if you need Docker inside the sandbox,
`shell` otherwise. Or extend either with your own image if the install
is heavy.
- **Install**: a `commands.install` block at runtime, or bake the agent
into a custom image. Pick install if it's a one-line script; bake if
the install is slow or you need a pinned version.
- **Network mapping**: list only the API host in `serviceDomains`, not
a wildcard. Keep install/CDN paths out of TLS-intercepting mode. Use
`deniedDomains` for hosts the agent should not reach.
- **Credential injection**: if the agent validates the API key's format
locally, register with `sbx secret set-custom` and pick a matching
placeholder. If it accepts the env var as-is, declare
`environment.proxyManaged` in the kit and skip the user-side step.
The rest — agent-context block, network-policy iteration, packaging — is the
same regardless of agent.
## Remove the stored secret
To remove the entry created earlier with `sbx secret set-custom`, pass
the host to `sbx secret rm`:
```console
$ sbx secret rm --host ampcode.com
```
The `--host` flag is part of the experimental `set-custom` surface and doesn't appear in `sbx secret rm --help`.

View File

@@ -1,467 +0,0 @@
---
title: Kit examples
linkTitle: Examples
description: Copy-and-adapt spec.yaml snippets for common mixin and sandbox kit patterns — static files, install commands, shell customization, background services, setup files, Claude Code skills, and agent forks.
keywords: sandboxes, sbx, kits, mixins, examples, patterns, skills
weight: 40
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
> [!NOTE]
> Kits are experimental. The kit file format, CLI commands, and experience
> for creating, loading, and managing kits are subject to change as the
> feature evolves. Share feedback and bug reports in the
> [docker/sbx-releases](https://github.com/docker/sbx-releases) repository.
Each section below shows one `spec.yaml` snippet that demonstrates a
single kit pattern. These aren't complete, distributable kits — they're
small, focused examples you can lift into your own kit. For the full
spec reference, see [Kit spec reference](kit-reference.md).
## Drop a shared config file
Use static files under `files/workspace/` when the content is the same
across every sandbox and doesn't need any runtime values substituted
in. Typical use cases: linter rules, editor settings, a shared
`.editorconfig`, team dotfiles.
```text
ruff-lint/
├── spec.yaml
└── files/
└── workspace/
└── ruff.toml
```
```yaml {title="ruff-lint/spec.yaml"}
schemaVersion: "2"
kind: mixin
name: ruff-lint
displayName: Ruff
description: Python linting with shared team config
setup:
install:
- command: "uv tool install ruff@latest"
user: "1000"
```
```toml {title="ruff-lint/files/workspace/ruff.toml"}
line-length = 80
[lint]
select = ["E", "F", "I"]
```
## Install a tool at sandbox creation
`setup.install` runs once per sandbox, at creation time. It's where
anything that needs to land in the image goes — package managers
(`apt-get`, `pip`, `npm`), binary downloads, or vendor install scripts.
> [!TIP]
> Each new sandbox runs all `setup.install` commands. The results aren't
> cached between sandboxes. Creating a kit avoids building and distributing an
> image, so kits work well for smaller, composable changes. For substantial
> build or installation steps, consider a
> [custom template](templates.md#build-a-custom-template). Sandboxes reuse
> template images from the local cache.
```yaml
setup:
install:
- command: "apt-get update && apt-get install -y jq"
- command: "curl -fsSL https://example.com/install.sh | sh"
```
Install commands run as root by default. Set `user: "1000"` when the
step should run as the agent user — for example, `npm install -g`
against a user-scoped prefix, or anything that writes to
`/home/agent/`.
Install steps run under `sh`, not bash, so bash-only builtins such as
`source` fail with `sh: source: not found`. Pipe explicitly to `bash`
(`curl … | bash`) or wrap the step in `bash -c '…'` when you need them.
Downloads are subject to the sandbox's
[network access rules](../governance/access-controls/network.md). A domain that
resolves from your host can still be blocked inside the sandbox — for
example, `get.sdkman.io` returns a 403 until you allow it with
`sbx policy allow network get.sdkman.io`. A tool may also need base
packages that aren't in the image: [SDKMAN!](https://sdkman.io/), for
instance, needs `zip` and `unzip`, so add an
`apt-get install -y zip unzip` step (as root) before installing it.
> [!WARNING]
> `curl … | bash` masks download failures. The pipe's exit status is
> bash's, and bash exits `0` on empty input, so a blocked or failed
> download still reports success — the sandbox is created with no error
> even though nothing was installed. Download first, then run, so a
> failed fetch fails the step:
>
> ```yaml
> setup:
> install:
> - command: "curl -fsSL https://example.com/install.sh -o /tmp/install.sh && bash /tmp/install.sh"
> user: "1000"
> ```
## Customize the shell environment
Some tools install into a versioned directory and expect you to source
an init script from your shell profile so their commands land on `PATH`.
Version managers like [nvm](https://github.com/nvm-sh/nvm) and
[SDKMAN!](https://sdkman.io/) follow this pattern. To make the tool
available in every shell, append the source line to
`/etc/sandbox-persistent.sh` in an install command.
`/etc/sandbox-persistent.sh` is the sandbox's persistent environment
file. It's sourced before every bash invocation — interactive shells and
non-interactive ones, including agents started with `sbx run` and
commands run with `sbx exec`. Appending here makes the tool available to
the agent regardless of how its shell is launched. Use
[`environment.variables`](kit-reference.md#environment) for ordinary variables
declared by a kit. To pass variables when creating a sandbox, use
[`-e` or `--env-file`](../usage.md#set-environment-variables).
```yaml {title="nvm/spec.yaml"}
schemaVersion: "2"
kind: mixin
name: nvm
displayName: nvm
description: Node version manager available in every shell
setup:
install:
- command: "curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash"
user: "1000"
description: Install nvm
- command: |
cat >> /etc/sandbox-persistent.sh <<'EOF'
export NVM_DIR="$HOME/.nvm"
unset NPM_CONFIG_PREFIX
[ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh"
EOF
user: "1000"
description: Source nvm for every shell
```
Both install steps run as `user: "1000"`. This installs the tool under
`/home/agent/` and can update `/etc/sandbox-persistent.sh`, which the agent user
owns. The `$HOME` in the appended lines resolves per user at source time, so the
agent user finds its own install. Append to the file rather than overwriting it
— the sandbox relies on its existing contents.
The base image ships its own Node and sets `NPM_CONFIG_PREFIX`, which
nvm won't activate alongside. `unset NPM_CONFIG_PREFIX` before sourcing
`nvm.sh` clears that conflict. Sourcing makes the `nvm` command
available; it doesn't put a Node version on `PATH`. Run
`nvm install --lts` to add one — wrap it in `bash -c '…'` if you script
it as an install step, since install steps run under `sh`.
Append only the init script, not the tool's tab-completion script.
Because `/etc/sandbox-persistent.sh` is sourced before every command,
completion scripts — which rely on variables that exist only during
completion — can break non-interactive shells that agents rely on.
## Install an internal CA certificate
If your organization uses a proxy that inspects HTTPS traffic, install
the proxy's internal root CA in the sandbox trust store. This helps
agents and SDKs trust certificates signed by the proxy.
```text
internal-ca/
├── spec.yaml
└── files/
└── home/
└── internal-ca.crt
```
Use a PEM-encoded certificate with a `.crt` extension. Files under
`files/home/` land in `/home/agent/` in the sandbox, so
`files/home/internal-ca.crt` becomes `/home/agent/internal-ca.crt` —
which is the path the install command reads from. If traffic can be
signed by more than one internal proxy, include each proxy's root CA in
the kit and install each certificate before running
`update-ca-certificates`.
```yaml {title="internal-ca/spec.yaml"}
schemaVersion: "2"
kind: mixin
name: internal-ca
setup:
install:
- command: "install -m 0644 /home/agent/internal-ca.crt /usr/local/share/ca-certificates/internal-ca.crt && update-ca-certificates"
user: "0"
description: Install internal CA certificate
```
`update-ca-certificates` adds the certificate to the system trust
store, so tools and SDKs that read the system bundle trust the proxy's
certificates without further configuration.
## Run a background service
`setup.startup` runs on every sandbox start. To keep a long-running
service such as a dev server or daemon alive, set `background: true`. The
sandbox runs the command in the background and replays startup commands on
each start, so the service comes back after a stop/start cycle:
```yaml
setup:
startup:
- command: ["my-service", "--port", "8080"]
user: "1000"
background: true
```
A background service doesn't write to your terminal. To capture its output
for debugging, wrap the command in a shell and redirect to a log file. Let
`background: true` run the command in the background rather than adding a
trailing `&` yourself:
```yaml
setup:
startup:
- command:
- sh
- -c
- my-service --port 8080 > /tmp/my-service.log 2>&1
user: "1000"
background: true
```
An empty log file tells you the wrapper ran; a populated one tells you why
the service failed.
## Write runtime values to a file
When a config file needs a value that isn't known until sandbox start
— most often the absolute workspace path — use `setup.files`.
The `${WORKDIR}` placeholder expands to the primary workspace path
when the file is written.
```yaml
setup:
files:
- path: /home/agent/.local/bin/start-code-server.sh
content: |
exec code-server --bind-addr 0.0.0.0:8080 --auth none "${WORKDIR}"
mode: "0755"
startup:
- command:
- sh
- -c
- nohup /home/agent/.local/bin/start-code-server.sh > /tmp/code-server.log 2>&1 &
user: "1000"
```
`mode: "0755"` makes the generated file executable so the startup
command can invoke it directly.
Use `setup.files` instead of a static file whenever the content depends
on a runtime value. Use a static file otherwise.
> [!TIP]
> This snippet is lifted from the
> [code-server kit](https://github.com/docker/sbx-kits-contrib/tree/main/code-server)
> in the contrib repository, which is also a runnable sample that demonstrates
> the full pattern.
## Ship a Claude Code skill
Claude Code reads project-scoped skills from
`.claude/skills/<name>/SKILL.md` in the workspace. Drop one into
`files/workspace/` and it's available in the sandbox.
```text
docker-review/
├── spec.yaml
└── files/
└── workspace/
└── .claude/
└── skills/
└── docker-review/
└── SKILL.md
```
```yaml {title="docker-review/spec.yaml"}
schemaVersion: "2"
kind: mixin
name: docker-review
displayName: Dockerfile review skill
description: Ships a Claude Code skill that reviews Dockerfiles
```
```markdown {title="docker-review/files/workspace/.claude/skills/docker-review/SKILL.md"}
---
name: docker-review
description: Review a Dockerfile for best practices. Use when the user asks to review, audit, or improve a Dockerfile.
---
When reviewing a Dockerfile, check:
1. Base image — pinned tag or digest, appropriate for the workload
2. Layer order — dependencies copied before application source
3. Image size — multi-stage builds, `.dockerignore`, package-manager cache flags
4. Security — non-root `USER`, no secrets in `ARG`/`ENV`
5. Reproducibility — pinned package versions, frontend directive where relevant
```
Kits have to target the workspace rather than `~/.claude/` because
sandboxes don't pick up user-level agent configuration from the host.
See the
[FAQ](../faq.md#why-doesnt-the-sandbox-use-my-user-level-agent-configuration)
for details.
## Customize agent settings
Some agents combine settings from several files. When the agent supports it,
place kit settings in a separate file instead of replacing
[sandbox-managed agent configuration](kits.md#sandbox-managed-agent-configuration).
Claude Code's `--settings` option loads an additional settings file. Extend the
built-in `claude` kit to add the option without reproducing its configuration,
and place the additional file outside the path the sandbox manages:
```text
claude-sonnet/
├── spec.yaml
└── files/
└── home/
└── .config/
└── claude/
└── sonnet.json
```
```yaml {title="claude-sonnet/spec.yaml"}
schemaVersion: "2"
kind: sandbox
name: claude-sonnet
extends: claude
sandbox:
command:
- --dangerously-skip-permissions
- --settings
- /home/agent/.config/claude/sonnet.json
```
```json {title="claude-sonnet/files/home/.config/claude/sonnet.json"}
{
"model": "sonnet"
}
```
Claude Code merges the additional file with the sandbox-managed user settings.
Because the file is under `files/home/`, it stays inside the sandbox instead of
being written into a directly mounted host workspace. Launch the sandbox by
passing the child kit directory in place of a built-in agent name:
```console
$ sbx run ./claude-sonnet
```
When you launch the kit for the first time, `sbx` prompts you to approve its
inherited Anthropic credentials. Because this is a third-party schema v2 kit,
`sbx` records your approval as a
[credential binding](../configuration/credentials.md#credential-bindings).
OpenCode supports an additional config file through `OPENCODE_CONFIG`. Keep the
kit's config separate from the sandbox-managed
`/home/agent/.config/opencode/opencode.json`, for example at
`/home/agent/.config/opencode/team.json`:
```text
opencode-team/
├── spec.yaml
└── files/
└── home/
└── .config/
└── opencode/
└── team.json
```
```yaml {title="opencode-team/spec.yaml"}
schemaVersion: "2"
kind: mixin
name: opencode-team
requires:
agent: opencode
environment:
variables:
OPENCODE_CONFIG: /home/agent/.config/opencode/team.json
```
```json {title="opencode-team/files/home/.config/opencode/team.json"}
{
"$schema": "https://opencode.ai/config.json",
"autoupdate": false
}
```
OpenCode merges the custom file with its global and project config files. See
the OpenCode [config precedence](https://opencode.ai/docs/config/#precedence-order)
for the complete order.
Agent settings mechanisms differ. If an agent doesn't support an additional
config file, launch option, or environment variable for the setting, kits can't
replace the sandbox-managed user settings before the agent launches.
`setup.startup` doesn't gate the agent entrypoint, so don't use it for settings
the agent must read during initialization.
## Fork an existing agent
Sandbox kits (`kind: sandbox`) define a full agent from scratch. The most
common variant is a fork of a built-in agent. Use `extends:` to inherit the
parent's complete configuration and declare only the fields you want to change.
This example replaces the built-in `claude` entrypoint so Claude Code uses
manual permission mode instead of bypassing approval prompts:
```yaml {title="claude-safe/spec.yaml"}
schemaVersion: "2"
kind: sandbox
name: claude-safe
displayName: Claude Code (with approval prompts)
description: Claude Code in manual permission mode
extends: claude
sandbox:
entrypoint: [claude, "--permission-mode", "manual"]
```
The child inherits the built-in image, credentials, network permissions,
persistent volumes, settings, MCP integration, agent instructions, setup
entries, and environment variables. Its `sandbox.entrypoint` replaces the
inherited entrypoint.
Launch by passing the sandbox kit in place of a built-in agent name:
```console
$ sbx run ./claude-safe
```
For a step-by-step walkthrough of building a new sandbox kit from
scratch, see [Build an agent](build-an-agent.md).
## More examples
These patterns are all drawn from working kits in the
[sbx-kits-contrib](https://github.com/docker/sbx-kits-contrib)
repository, which contains each example as a complete, loadable kit.
Use it to study the full shape of a kit. Load a mixin with `--kit`:
```console
$ sbx run claude --kit "git+https://github.com/docker/sbx-kits-contrib.git#dir=<kit>"
```
For a `kind: sandbox` kit, pass the reference in place of the agent name:
```console
$ sbx run "git+https://github.com/docker/sbx-kits-contrib.git#dir=<kit>"
```

View File

@@ -1,96 +1,183 @@
---
title: Kit spec reference
linkTitle: Spec reference
description: Field-by-field reference for a kit's spec.yaml, including arguments, credentials, network rules, environment, setup, files, agent instructions, and the sandbox block.
keywords: sandboxes, sbx, kits, spec.yaml, reference, schema, fields
weight: 50
title: Kits v2
linkTitle: Kits v2
description: Reference for v2 kits, including usage, schema fields, maintenance examples, signing, and migration to v3.
keywords: sandboxes, sbx, kits, v2, migration, spec.yaml
weight: 60
aliases:
- /ai/sandboxes/customize/kits/
- /ai/sandboxes/customize/kit-reference/
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
> [!NOTE]
> Kits are experimental. The kit file format, CLI commands, and experience
> for creating, loading, and managing kits are subject to change as the
> feature evolves. Share feedback and bug reports in the
> [docker/sbx-releases](https://github.com/docker/sbx-releases) repository.
V2 kits remain supported. This page covers v2 usage, configuration, and the
specification. For new kit development, use [v3 kits](/manuals/ai/sandboxes/customize/_index.md).
This page documents every field in a kit's `spec.yaml`. For an overview of
what kits are and how to use them, see [Kits](kits.md).
Built-in shortcuts such as `claude` and `codex` select v2 kits and still work
with v2 mixins. V3 workloads and mixins can't be combined with v1 or v2 kits.
V1 also remains supported.
For the normative v2 grammar used by the parser and tests, see the
[`schemaVersion: "2"` specification](https://github.com/docker/sbx-kits-contrib/blob/main/spec/SPEC-v2.md)
in the `docker/sbx-kits-contrib` repository.
## Use existing kits
A kit directory has a required `spec.yaml` and an optional `files/` tree:
A v2 kit contains `spec.yaml` with `schemaVersion: "2"` and an optional `files/`
tree. A sandbox kit defines the agent environment. A mixin adds tools or
configuration to it. Pass a sandbox kit in place of the agent name and add
mixins with `--kit`:
```text
my-kit/
├── spec.yaml # required
└── files/ # optional — static files to inject
├── home/
└── workspace/
```console
$ sbx run ./my-agent --name my-project --kit ./team-config
$ sbx run claude --name claude-project --kit ./team-config
```
## Schema versions
`sbx run` uses your current directory as the workspace. Append a project path
to use another directory. To create without launching the agent, use
`sbx create`; include a workspace path or `.` to mount a directory.
References can be local directories, ZIP files, OCI artifacts, or Git URLs.
Start relative paths with `./` or `../`. For Docker Hub kits, you can omit
`docker.io/` and use `<NAMESPACE>/<KIT>:<TAG>`. In Git URLs, `ref` selects a
revision and `dir` the kit directory. Quote URLs containing `&`:
Starting with Docker Sandboxes version 0.36, two schema versions are supported.
Use `schemaVersion: "2"` for new kits. Version `"1"` remains accepted through
the legacy path.
```console
$ sbx run "git+https://github.com/<ORG>/<REPOSITORY>.git#ref=<COMMIT>&dir=my-agent"
```
When migrating to `schemaVersion: "2"`, replace v1 fields with their v2
equivalents:
`git+ssh://` URLs work with your local SSH agent and Git credentials.
For private registries, see
[Registry credentials](../configuration/credentials.md#registry-credentials).
| v1 | v2 |
| ------------------------------------------- | ---------------------------------------- |
| `credentials.sources.<id>` | `credentials:` list entry with `service` |
| `network.allowedDomains` / `deniedDomains` | `permissions.network.allow` / `deny` |
| `network.serviceDomains` / `serviceAuth` | `credentials[].apiKey.inject` |
| `network.publishedPorts` / `publishedPorts` | top-level `ports` |
| standalone `oauth:` block | `credentials[].oauth` |
| `oauth.skipIfEnv` | Accepted but ignored |
| `environment.proxyManaged` | `credentials[].apiKey.proxyManaged` |
| `memory` / `agentContext` | `agentInstructions.content` |
| `kind: agent` / `agent:` block | `kind: sandbox` / `sandbox:` block |
| `sandbox.aiFilename` | `agentInstructions.filename` |
| `sandbox.entrypoint.run` | `sandbox.entrypoint` |
| `sandbox.entrypoint.args` | `sandbox.command.default` |
| `sandbox.entrypoint.ttyArgs` | `sandbox.command.interactive` |
| `tmpfs:` | `volumes:` entries with `type: tmpfs` |
| `volumes:` (mapping form) | `volumes:` sequence (`- path: <path>`) |
| `commands:` / `commands.initFiles` | `setup:` / `setup.files` |
| `settings:` / `kitDir` / `persistence` | Removed |
Kit selection with `--kit` applies at creation. Recreate the sandbox to change
its kit set, except for the limited updates supported by
[`sbx kit add`](#execution-order). That command restarts the sandbox while
preserving packages, images, volumes, and agent history. Kits can't be
removed from a running sandbox.
Credential discovery also moved out of the kit in v2: a kit declares which
credentials it needs and how to inject them, but where each value comes from is
controlled by the user through
[credential bindings](../configuration/credentials.md#credential-bindings).
### Restrict kit sources
> [!NOTE]
> `mixins` and `sandbox.build` are accepted by the parser, but runtime support
> is pending. A kit that sets `sandbox.build` must also set `sandbox.image`.
See [Restrict kit sources](/manuals/ai/sandboxes/customize/use-kits.md#restrict-kit-sources)
for source policies. `kit.allowLocalKits` also governs v2 ZIP files.
## Top-level fields
## Image overrides for built-in agents
Use `--template` to replace a built-in agent's image while keeping its
configuration and launch command. The replacement image must support the
same agent. For example, an image used with `claude` must have Claude Code
installed.
To define an environment with its own launch command and sandbox settings,
see [Build an agent workload](/manuals/ai/sandboxes/customize/author/build-an-agent.md)
for the v3 workflow.
### Choose a template
Docker publishes agent images as `docker/sandbox-templates:<variant>`.
Choose the variant that matches your agent. See
[Base images](/manuals/ai/sandboxes/customize/author/base-images.md)
for the available variants.
Variants with a `-docker` suffix, such as `claude-code-docker`, include
Docker Engine for building and running containers inside the sandbox.
Built-in agents use these variants by default when you don't specify a
custom template.
If you don't need Docker inside the sandbox, select a variant without
the suffix. It uses fewer resources and doesn't require privileged mode:
```console
$ sbx run claude --template docker.io/docker/sandbox-templates:claude-code
```
Include the registry domain in `--template` image references. Unlike kit
references, template references don't automatically expand to include
`docker.io`.
### Build a custom template
Building a custom template requires
[Docker Desktop](/manuals/desktop/_index.md).
Extend the Docker-provided image for the agent you plan to run. For example,
this Dockerfile adds Rust and protocol buffer tools to the Claude Code image:
```dockerfile
FROM docker/sandbox-templates:claude-code
USER root
RUN apt-get update && apt-get install -y protobuf-compiler
USER agent
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
```
Install system packages as `root`, then switch back to `agent` before
installing tools in the agent's home directory.
Build the image and push it to a registry. Replace `<NAMESPACE>` with a
Docker Hub namespace you can push to:
```console
$ docker build -t docker.io/<NAMESPACE>/my-template:v1 --push .
```
For registry credentials and loading a locally built image, see
[Load a template](/manuals/ai/sandboxes/usage.md#load-a-template).
Run the sandbox with your image:
```console
$ sbx run claude --template docker.io/<NAMESPACE>/my-template:v1
```
Because this image extends `claude-code`, use it with `claude`. For an
image based on `codex`, use `codex`. For one based on `shell`, use `shell`
to open Bash without an agent.
If your added tools need network access, allow the domains they use in
the sandbox's network policy, unless you use the `allow-all` policy:
```console
$ sbx policy allow network "*.example.com:443,example.com:443"
```
## Kit kinds
### `kind: mixin`
A mixin layers capabilities onto an existing sandbox. It must not declare a
`sandbox:` block, `extends:`, or `mixins:`. A mixin can declare `requires:` to
pin the base agent it is designed for:
```yaml
schemaVersion: "2"
kind: <mixin | sandbox>
name: <name>
version: <version>
displayName: <name>
description: <text>
sourceURL: <url>
licenses:
- MIT
locked:
- sandbox.image
security:
privileged: false
args:
channel:
default: stable
enum: [stable, beta]
kind: mixin
name: github-tools
requires:
agent: claude
```
`requires.agent` takes one base-agent name. It is validated as a kit name and
enforced during composition.
### `kind: sandbox`
A sandbox kit defines a full agent. A root sandbox must declare a `sandbox:`
block. A sandbox that uses `extends:` can inherit the parent image and omit its
own `sandbox:` block:
```yaml
schemaVersion: "2"
kind: sandbox
name: claude-safe
extends: claude
```
`extends:` is sandbox-only. The parent must resolve to a sandbox kit. `mixins:`
is also sandbox-only and accepted by the parser, but runtime composition support
is pending.
## Top-level fields
For the normative grammar, see the
[v2 specification](https://github.com/docker/sbx-kits-contrib/blob/main/spec/SPEC-v2.md).
| Field | Required | Description |
| --------------- | -------- | ----------------------------------------------------------------------------------------------- |
| `schemaVersion` | Yes | Spec schema version. Use `"2"` for this grammar. |
@@ -152,45 +239,23 @@ Argument values are strings, but substitution happens before YAML decoding.
Quote a placeholder in a string-valued field so a value such as `1.20` isn't
decoded as a number.
Supply values with `--kit-arg` or `--kit-args-file` when loading the kit. See
[Pass arguments to kits](kits.md#pass-arguments-to-kits) for scoping,
precedence, and validation behavior.
### Pass arguments to kits
## Kit kinds
Use `--kit-arg name=value` for every kit declaring that argument, or prefix
with the kit's `name` to target one kit. Scoped values override shared values:
### `kind: mixin`
A mixin layers capabilities onto an existing sandbox. It must not declare a
`sandbox:` block, `extends:`, or `mixins:`. A mixin can declare `requires:` to
pin the base agent it is designed for:
```yaml
schemaVersion: "2"
kind: mixin
name: github-tools
requires:
agent: claude
```console
$ sbx run ./my-agent --kit ./my-mixin --kit-arg channel=stable \
--kit-arg my-mixin.channel=beta
```
`requires.agent` takes one base-agent name. It is validated as a kit name and
enforced during composition.
### `kind: sandbox`
A sandbox kit defines a full agent. A root sandbox must declare a `sandbox:`
block. A sandbox that uses `extends:` can inherit the parent image and omit its
own `sandbox:` block:
```yaml
schemaVersion: "2"
kind: sandbox
name: claude-safe
extends: claude
```
`extends:` is sandbox-only. The parent must resolve to a sandbox kit. `mixins:`
is also sandbox-only and accepted by the parser, but runtime composition support
is pending.
`--kit-args-file <FILE>` reads `name=value` entries, ignoring blank lines and
`#` comments. Later files override earlier files; `--kit-arg` overrides files.
For repeated CLI keys, the last value wins. Missing required values, unknown
arguments, undeclared placeholders, and invalid values fail before creation.
Pass the same flags to `sbx kit validate` or `sbx kit inspect` when needed.
Argument values can remain in shell history and are stored unencrypted in
argument files.
## Sandbox block
@@ -245,12 +310,7 @@ requirements.
## Agent instructions
```yaml
agentInstructions:
filename: CLAUDE.md
content: |
Ruff is installed. Run `ruff check` before committing.
```
Declare these fields under `agentInstructions`:
| Field | Description |
| ---------- | --------------------------------------------------------------------------------------------------- |
@@ -262,6 +322,11 @@ For mixins, the engine writes `content` to
section to the base AI file. This keeps each mixin's instructions in a separate
file.
The generated profile lives in the parent directory of the mounted workspace
inside the sandbox. It sits outside the mount and doesn't replace an
instruction file in the project. The sandbox kit's inline instructions go
directly into that profile.
## Credentials
A kit declares the credentials it needs and how the proxy injects them into
@@ -270,41 +335,6 @@ provides the value through the secret store or the first-run prompt, and a
[credential binding](../configuration/credentials.md) authorizes its use. A kit
can't read arbitrary host environment variables or files.
```yaml
credentials:
- service: <service-id>
description: <text> # optional
required: <true | false> # optional, default false
provider: <provider> # optional, reserved
apiKey:
name: <ENV_VAR>
proxyManaged: true
inject:
- domain: <domain>
header: <header>
format: <format>
- domain: <domain>
scheme: bearer
- domain: <domain>
scheme: basic
username: <user> # required with scheme: basic
oauth:
tokenEndpoint:
host: <host>
path: <path>
sentinels:
accessToken: <sentinel>
refreshToken: <sentinel>
credentialFile:
path: <path>
structure:
<key>:
accessToken: "{{.AccessToken}}"
refreshToken: "{{.RefreshToken}}"
expiresAt: "{{.ExpiresAt}}"
scopes: "{{.Scopes}}"
```
`credentials` is a list; each entry names a `service` and configures one or more
auth mechanisms.
@@ -363,13 +393,6 @@ their own domain mapping — the proxy injects a credential only into the domain
its [`apiKey.inject`](#apikey) lists, and every domain the
sandbox reaches must be allowed here.
```yaml
permissions:
network:
allow: [<domain>, ...]
deny: [<domain>, ...]
```
| Field | Description |
| --------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `permissions.network.allow` | Domains the sandbox can reach. |
@@ -392,13 +415,7 @@ In v1 this was the `network:` block (`allowedDomains` / `deniedDomains`, plus
## Ports
Use `ports` to expose sandbox services to the host:
```yaml
ports:
- container: 8080
name: web
```
Declare `ports` as a list of entries to expose sandbox services to the host:
| Field | Description |
| ----------- | ------------------------------------------------------------------- |
@@ -415,15 +432,9 @@ in the sandbox is listening there. Users can pin host ports with
## Environment
```yaml
environment:
variables:
<NAME>: <value>
```
| Field | Description |
| ----------- | ---------------------------------------------- |
| `variables` | Key-value pairs set directly in the container. |
| `environment.variables` | Key-value pairs set directly in the container. |
Do not set `DASH_`, `SBX_`, or `DOCKER_` variables, and avoid overriding
`HOME`, `USER`, `SHELL`, `PATH`, `LD_PRELOAD`, and `LD_LIBRARY_PATH`. The
@@ -431,24 +442,8 @@ runtime reserves these names and may override them.
## Setup
```yaml
setup:
install:
- command: <shell-string>
user: <uid>
description: <text>
startup:
- command: [<argv>, ...]
user: <uid>
background: <true | false>
description: <text>
files:
- path: <path>
content: <text>
mode: <octal>
onlyIfMissing: <true | false>
description: <text>
```
`setup.install`, `setup.startup`, and `setup.files` are lists of commands or
files with the fields described here.
### Execution order
@@ -534,6 +529,14 @@ path must be writable by that user. To write to a root-owned path such as
`/etc`, use an `install` command, which runs as root by default. Set ownership
in the install command if the agent needs to modify the file later.
### Shell initialization and service logs
With Docker templates, append shell initialization to
`/etc/sandbox-persistent.sh` in an install command. Keep existing content and
omit completion scripts: interactive and non-interactive Bash commands source
this file. For a background service, redirect startup output to a file and
read it with `sbx exec`. Use `background: true` instead of a trailing `&`.
## Static files
```text
@@ -551,18 +554,13 @@ Parent directories are created automatically. Existing files are
overwritten. Absolute paths and path-traversal sequences (`../../`) are
rejected.
Static files can supply linter settings, helper scripts, or agent skills.
For example, a Claude Code project skill belongs at
`files/workspace/.claude/skills/<NAME>/SKILL.md`.
## Volumes
```yaml
volumes:
- path: /workspace
size: 10g
mode: "0755"
- path: /tmp/scratch
type: tmpfs
size: 512m
mode: "1777"
```
Declare `volumes` as a list of mounts with these fields:
| Field | Description |
| ------ | ------------------------------------------------------------------- |
@@ -573,3 +571,231 @@ volumes:
Volumes are applied only when a sandbox is created. `sbx kit add` cannot attach
volumes to a running container.
## Fork an existing agent
Sandbox kits (`kind: sandbox`) define a full agent from scratch. The most
common variant is a fork of a built-in agent. Use `extends:` to inherit the
parent's complete configuration and declare only the fields you want to change.
This example replaces the built-in `claude` entrypoint so Claude Code uses
manual permission mode instead of bypassing approval prompts:
```yaml {title="claude-safe/spec.yaml"}
schemaVersion: "2"
kind: sandbox
name: claude-safe
displayName: Claude Code (with approval prompts)
description: Claude Code in manual permission mode
extends: claude
sandbox:
entrypoint: [claude, "--permission-mode", "manual"]
```
The child inherits the built-in image, credentials, network permissions,
persistent volumes, settings, MCP integration, agent instructions, setup
entries, and environment variables. Its `sandbox.entrypoint` replaces the
inherited entrypoint.
Launch by passing the sandbox kit in place of a built-in agent name:
```console
$ sbx run ./claude-safe
```
## Install an internal CA certificate
Put each PEM-encoded root certificate under `files/home/` with a `.crt`
extension. For `files/home/internal-ca.crt`, use:
```yaml {title="internal-ca/spec.yaml"}
schemaVersion: "2"
kind: mixin
name: internal-ca
setup:
install:
- command: "install -m 0644 /home/agent/internal-ca.crt /usr/local/share/ca-certificates/internal-ca.crt && update-ca-certificates"
user: "0"
```
This updates the system trust store. For several CAs, install every
certificate before running `update-ca-certificates`.
## Sandbox-managed agent configuration
Built-in agent kits reserve the following paths for sandbox setup. Treat these
paths as sandbox-managed, even if a file is only needed for a particular
feature. Don't target them with static files, `setup.files`, or install
commands. Later setup can replace your content or depend on settings that your
file removes. In this table, `~` is `/home/agent`.
| Built-in agent kit | Managed configuration paths |
| ------------------ | --------------------------- |
| `claude` | `~/.claude.json`, `~/.claude/settings.json`, `~/.claude/.config.json` |
| `codex` | `~/.codex/config.toml` |
| `copilot` | `~/.copilot/config.json` |
| `cursor` | `~/.cursor/cli-config.json` |
| `devin` | `~/.config/devin/config.json`, `~/.config/devin/mcp_config.json` |
| `gemini` | `~/.gemini/settings.json` |
| `kiro` | `~/.kiro/settings/mcp.json` |
| `opencode` | `~/.config/opencode/opencode.json` |
Use separate settings files when supported: Claude Code accepts `--settings`,
and OpenCode reads `OPENCODE_CONFIG`. Don't use `setup.startup` for settings
the agent must read during initialization; startup commands don't gate the
entrypoint.
## Packaging and distribution
The `sbx kit` subcommands validate, inspect, and publish kits:
- `sbx kit validate <path>` — check that a kit directory or ZIP is
well-formed.
- `sbx kit inspect <path>` — display kit details. Add `--json` for
machine-readable output.
- `sbx kit pack <path> -o <file.zip>` — package a directory as a ZIP file
for sharing.
- `sbx kit push <path> <ref>` — publish to an OCI registry (for example,
`ghcr.io/myorg/my-kit:1.0`).
- `sbx kit pull <ref>` — download a kit from a registry as a ZIP file to
the working directory.
For Docker Hub, `sbx kit pull` and `sbx kit push` use the session from
`sbx login`. For other registries, they prefer credentials stored with
[`sbx secret set --registry`](../configuration/credentials.md#registry-credentials).
Both commands fall back to the Docker credential store, so credentials from
`docker login` also work.
## Sign and verify kits
Use cosign-compatible Sigstore signatures to verify who approved a kit and
that its signed content hasn't changed. Signing is keyless by default. Verify a
keyless signature with the certificate identity and OpenID Connect (OIDC)
issuer:
```console
$ sbx kit sign ./my-kit/
$ sbx kit verify \
--certificate-identity user@example.com \
--certificate-oidc-issuer https://accounts.google.com \
./my-kit/
```
For key-based signing, use an ECDSA P-256 key pair:
```console
$ sbx kit sign --key cosign.key ./my-kit/
$ sbx kit verify --key cosign.pub ./my-kit/
```
For a local directory, `sbx kit sign` writes a `kit.sig.bundle` file next to
`spec.yaml`. Commit this file so consumers can verify a kit loaded from the Git
repository. For an OCI kit, the signature is stored as an OCI referrer. You can
sign an OCI kit after pushing it, or push and sign it in one step:
```console
$ sbx kit push ./my-kit/ ghcr.io/myorg/my-kit:1.0 --sign
```
ZIP kits can't carry verifiable signatures.
### Require signed kits
Set [`kit.trustedSigners`](../configuration/settings.md#kittrustedsigners) to
the identities or keys you trust before requiring signatures. Otherwise, `sbx` uses the default policy, which trusts
Docker employee identities attested by Google's OpenID Connect issuer. A
keyless policy must specify both the certificate identity and its OpenID
Connect issuer:
```console
$ sbx settings set kit.trustedSigners \
'[{"identity":"release-bot@example.com","issuer":"https://accounts.google.com"}]'
$ sbx settings set kit.requireSignature true
```
To trust a key-based signature, set the policy to the public key path:
```console
$ sbx settings set kit.trustedSigners '[{"key":"/path/to/cosign.pub"}]'
$ sbx settings set kit.requireSignature true
```
When `kit.requireSignature` is `true`, `sbx` rejects unsigned kits, signatures
that don't match `kit.trustedSigners`, and ZIP kits. This policy applies when a
kit is loaded from a local directory, Git repository, or OCI registry.
The signature covers `spec.yaml` and the kit's `files/` content, but not mutable
dependencies such as image tags or content downloaded by install and startup
commands. Pin those dependencies by digest or checksum when they must remain
immutable.
## Schema versions
Schema v2 is supported starting with Docker Sandboxes version 0.36. Use
`schemaVersion: "2"` for the syntax on this page. Version `"1"` also remains
accepted. V3 is a separate format for environments built entirely
with v3 workloads and mixins. V3 kits can't compose with v1 or v2 kits.
See [Kits v3](/manuals/ai/sandboxes/customize/_index.md) for that workflow.
When migrating to `schemaVersion: "2"`, replace v1 fields with their v2
equivalents:
| v1 | v2 |
| ------------------------------------------- | ---------------------------------------- |
| `credentials.sources.<id>` | `credentials:` list entry with `service` |
| `network.allowedDomains` / `deniedDomains` | `permissions.network.allow` / `deny` |
| `network.serviceDomains` / `serviceAuth` | `credentials[].apiKey.inject` |
| `network.publishedPorts` / `publishedPorts` | top-level `ports` |
| standalone `oauth:` block | `credentials[].oauth` |
| `oauth.skipIfEnv` | Accepted but ignored |
| `environment.proxyManaged` | `credentials[].apiKey.proxyManaged` |
| `memory` / `agentContext` | `agentInstructions.content` |
| `kind: agent` / `agent:` block | `kind: sandbox` / `sandbox:` block |
| `sandbox.aiFilename` | `agentInstructions.filename` |
| `sandbox.entrypoint.run` | `sandbox.entrypoint` |
| `sandbox.entrypoint.args` | `sandbox.command.default` |
| `sandbox.entrypoint.ttyArgs` | `sandbox.command.interactive` |
| `tmpfs:` | `volumes:` entries with `type: tmpfs` |
| `volumes:` (mapping form) | `volumes:` sequence (`- path: <path>`) |
| `commands:` / `commands.initFiles` | `setup:` / `setup.files` |
| `settings:` / `kitDir` / `persistence` | Removed |
Credential discovery also moved out of the kit in v2: a kit declares which
credentials it needs and how to inject them, but where each value comes from is
controlled by the user through
[credential bindings](../configuration/credentials.md#credential-bindings).
> [!NOTE]
> `mixins` and `sandbox.build` are accepted by the parser, but runtime support
> is pending. A kit that sets `sandbox.build` must also set `sandbox.image`.
## Move an environment to v3
Select a v3 workload, convert or replace its mixins, and create a separate
sandbox with a different `--name`. Use the explicit workload reference in
place of the built-in shortcut. Every selected kit must use v3.
Changing `schemaVersion` alone doesn't convert a kit. Separate reusable image
content from sandbox initialization, and declare runtime capabilities:
| V2 surface | V3 equivalent |
| --- | --- |
| `kind: sandbox` | `kind: workload` with a Dockerfile recipe |
| `sandbox.image` | Dockerfile `FROM` |
| `sandbox.entrypoint`, `sandbox.command`, `environment.variables` | Dockerfile `ENTRYPOINT`, `CMD`, and `ENV` |
| `extends` | A mixin for composition, or a derived workload image with its own descriptor |
| `setup.install` | Dockerfile `RUN` for reusable content; lifecycle `install` for sandbox initialization |
| `setup.startup` and `setup.files` | Lifecycle capability `startup` and `files` |
| `setup.files[].onlyIfMissing: true` | Lifecycle `files[].overwrite: false` |
| Automatic `files/home/` and `files/workspace/` injection | Dockerfile `COPY`, with lifecycle hooks for destinations provided by runtime mounts |
| `permissions.network` and `credentials` | Network-policy and credential capabilities |
| `agentInstructions` | Agent-context capability |
Follow the [v3 authoring guidance](/manuals/ai/sandboxes/customize/author/_index.md)
when converting runtime setup and capability declarations.
Use a [kit set](/manuals/ai/sandboxes/customize/author/kit-sets.md) to combine
published v3 components with your settings. To rebuild the agent environment
from a base image, follow [Build an agent workload](/manuals/ai/sandboxes/customize/author/build-an-agent.md).
Running an existing sandbox keeps its recorded configuration. It doesn't
migrate the kit composition.

View File

@@ -1,631 +0,0 @@
---
title: Kits
description: Extend a sandbox with tools, credentials, network rules, and configuration using declarative YAML artifacts.
keywords: sandboxes, sbx, kits, mixins, customization, extensions, agents
weight: 20
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
> [!NOTE]
> Kits are experimental. The kit file format, CLI commands, and experience
> for creating, loading, and managing kits are subject to change as the
> feature evolves. Share feedback and bug reports in the
> [docker/sbx-releases](https://github.com/docker/sbx-releases) repository.
A kit packages a set of capabilities a sandbox can use, such as:
- Tools to install
- Environment variables to set
- Credentials to inject
- Network rules to allow or deny domains
- Files to drop in
- Startup commands to run
- Memory instructions to give the agent
You declare these in a single `spec.yaml` file, point the CLI at the
directory (or a ZIP, OCI artifact, or Git URL), and the sandbox applies
and enforces them at runtime. Credentials stay on the host and go through
a proxy instead of entering the VM, and outbound traffic is restricted to
the domains permitted by the kit's network rules.
A kit is either a mixin or a sandbox:
- Mixin kits (`kind: mixin`) extend an existing agent with extra
capabilities. Stack several on the same sandbox.
- Sandbox kits (`kind: sandbox`) define a full agent from scratch: its image,
entrypoint, network policies, and everything else the agent needs.
## What kits can do
### Run commands
A kit can run commands inside the sandbox automatically. **Install
commands** run once at creation; **startup commands** run each time
the sandbox starts.
Install commands are the place to put anything an agent needs into the
image, via `apt`, `pip`, `npm`, `curl | bash`, or whatever fits:
```yaml
setup:
install:
- command: "apt-get update && apt-get install -y jq"
```
Startup commands are for work that can run alongside the agent, such as a
background service. They must be idempotent — see the
[`startup`](kit-reference.md#startup) spec reference:
```yaml
setup:
startup:
- command: ["my-daemon"]
background: true
```
### Inject files
Kits can inject files into the sandbox in two ways: **static files** bundled
with the kit, and **`setup.files`** written at startup with runtime values
substituted in.
Static files work well for content that doesn't vary between sandboxes, such
as tool configurations, shared linter rules, helper scripts the agent can
invoke, or reference material like a style guide or API cheatsheet.
```text
my-kit/
├── spec.yaml
└── files/
├── home/
│ └── .config/my-tool/settings.json
└── workspace/
└── .editorconfig
```
`setup.files` cover content that depends on runtime values, such as an
absolute workspace path that a tool needs to bake into its config file
at startup:
```yaml
setup:
files:
- path: /home/agent/.my-tool/config.json
content: '{"workspace": "${WORKDIR}"}'
onlyIfMissing: true
```
See [`setup.files`](kit-reference.md#files) in the spec reference for all
fields.
#### Sandbox-managed agent configuration
Built-in agent kits reserve the following paths for sandbox setup. Treat these
paths as sandbox-managed, even if a file is only needed for a particular
feature. Don't target them with static files, `setup.files`, or install
commands. Later setup can replace your content or depend on settings that your
file removes. In this table, `~` is `/home/agent`.
| Built-in agent kit | Managed configuration paths |
| ------------------ | --------------------------- |
| `claude` | `~/.claude.json`, `~/.claude/settings.json`, `~/.claude/.config.json` |
| `codex` | `~/.codex/config.toml` |
| `copilot` | `~/.copilot/config.json` |
| `cursor` | `~/.cursor/cli-config.json` |
| `devin` | `~/.config/devin/config.json`, `~/.config/devin/mcp_config.json` |
| `gemini` | `~/.gemini/settings.json` |
| `kiro` | `~/.kiro/settings/mcp.json` |
| `opencode` | `~/.config/opencode/opencode.json` |
Use a separate settings layer when the agent supports one. For example, Claude
Code can load an additional settings file with `--settings`, and OpenCode can
load one from the path in `OPENCODE_CONFIG`. See
[Customize agent settings](kit-examples.md#customize-agent-settings) for
examples. Don't use `setup.startup` for settings the agent must read during
initialization because startup commands don't gate the agent entrypoint.
### Set environment variables
Environment variables set by the kit are available to the agent at
runtime:
```yaml
environment:
variables:
MY_TOOL_WORKSPACE: /home/agent/my-tool
```
For credentials, see
[Authenticate to external services](#authenticate-to-external-services).
Don't put secret values directly in `environment.variables` — they'd
be visible inside the sandbox VM.
> [!IMPORTANT]
> The sandbox manages proxy settings for you. It sets `HTTP_PROXY`,
> `HTTPS_PROXY`, `NO_PROXY`, and their lowercase equivalents automatically so
> that traffic flows through its built-in forward proxy, which enforces
> network policy and injects credentials. Leave these variables to the
> sandbox — setting them in a kit points traffic away from the forward proxy,
> so it can no longer apply network policy or inject credentials, and those
> requests typically fail to connect. To send sandbox traffic through an
> upstream corporate proxy, configure it on the host. See
> [Upstream proxy](../architecture.md#upstream-proxy).
### Control network access
Network rules define which domains the sandbox can reach or block. Kit
network rules apply only to sandboxes that use the kit:
```yaml
permissions:
network:
allow:
- api.example.com
- "*.cdn.example.com"
deny:
- telemetry.example.com
```
Use `allow` for hosts the agent needs, such as package
registries, install endpoints, or external APIs. Use `deny` for
hosts the agent should not reach, such as telemetry endpoints. If a domain
matches both an allow rule and a deny rule, the deny rule wins.
> [!IMPORTANT]
> When organization governance is active, only organization allow rules grant
> access, so kit-defined `allow` rules are ignored — including any domains a kit
> allows for the agent to reach. Kit-defined `deny` rules still apply, because a
> deny can only restrict access further. For details, see
> [Policy precedence](../governance/concepts.md#precedence).
For authenticated services, see
[Authenticate to external services](#authenticate-to-external-services).
### Authenticate to external services
A kit can attach credentials to outbound requests through the
host-side proxy. The agent inside the VM works with a sentinel value;
the proxy reads the real credential on the host and overwrites the
auth header before the request leaves the sandbox.
A kit declares the service, the in-container environment variable, and how
to inject the credential. It doesn't declare a host discovery source. The user
provides the value through the secret store or first-run prompt, and a
[credential binding](../configuration/credentials.md) authorizes its use:
```yaml
credentials:
- service: my-service
apiKey:
name: MY_SERVICE_API_KEY # in-VM env var, set to a sentinel
proxyManaged: true
inject:
- domain: api.example.com # inject on requests to this domain
header: Authorization # overwrite this header
format: "Bearer %s"
permissions:
network:
allow:
- api.example.com # the domain must also be reachable
```
The agent boots with `MY_SERVICE_API_KEY=proxy-managed`, sends a
request with that sentinel in `Authorization`, and the proxy overwrites
the header with the real credential before forwarding. The real
secret never enters the VM.
See [Credentials](../configuration/credentials.md) for how to provide the
credential value on your host, other approaches for cases the example
above doesn't fit, and what the proxy does at request time. See
[Credential bindings](../configuration/credentials.md) to approve the mechanisms
and domains declared by a third-party v2 kit.
### Inject agent memory
A kit can append content to the agent's memory file, such as `CLAUDE.md`
or `AGENTS.md`. The agent reads this file at startup. Use it to give
the agent project conventions, usage tips for a tool the kit installs,
or other guidance that should be in scope when the sandbox runs.
```yaml
agentInstructions:
content: |
Ruff is installed. Run `ruff check` before committing.
Shared config lives at `/workspace/ruff.toml`.
```
Both mixin and sandbox kits can declare `agentInstructions.content`. The active
sandbox kit sets `agentInstructions.filename`, which determines the memory
file's name. The sandbox kit's content is written inline in that file. Each
mixin's content is written to its own `<kit-name>.md` file under a sibling
`kits-memory/` directory, and the main memory file gets a `## Kits` section that
points to each mixin file:
```text
/Users/you/
├── myproject/ # workspace
├── AGENTS.md # main memory file with a "## Kits" index
└── kits-memory/
├── ruff-lint.md
├── vale.md
└── git-ssh-sign.md
```
See [`agentInstructions`](kit-reference.md#agent-instructions) in the spec
reference for the full field schema.
### Define an agent
Sandbox kits declare a `sandbox:` block with the image the agent runs in and
the command the user attaches to when they launch the sandbox:
```yaml
sandbox:
image: "my-registry/my-agent:latest"
entrypoint: [my-agent, "--yolo"]
```
See [Sandbox kits](#sandbox-kits) for use cases and an example.
## Mixin kits
A mixin kit extends an existing agent with extra capabilities. Common use
cases:
- Pre-install tools: linters, libraries, or other custom programs
- Grant the agent access to a new authenticated service (a database, a
vendor API)
- Inject shared team config (linter rules, editor settings, dotfiles)
See [Drop a shared config file](kit-examples.md#drop-a-shared-config-file) and
[Install a tool at sandbox creation](kit-examples.md#install-a-tool-at-sandbox-creation)
for complete mixin examples.
## Sandbox kits
A sandbox kit defines a full agent from scratch — image, entrypoint, and
everything the agent needs. Common use cases:
- Package a custom agent you've built so others can run it
- Ship a team-internal agent with defaults baked in
- Run a fork of an existing agent with your own config
- Prototype a new agent integration
Sandbox kits declare everything a mixin kit can, plus an
[`sandbox:` block](kit-reference.md#sandbox-block) that tells the sandbox how to launch the
agent. For a step-by-step walkthrough, see
[Build your own agent kit](build-an-agent.md).
### Extend a built-in agent
Use `extends:` to create a variant of a built-in agent without reproducing its
configuration. The child kit inherits the parent's image, credentials, network
permissions, persistent volumes, settings, MCP integration, and agent
instructions. It also inherits the parent's environment variables and all
`setup.install`, `setup.startup`, and `setup.files` entries. Parent setup entries
run before child entries. If both kits set the same environment variable, the
child's value wins. Use `extends:` for a single parent agent; use a mixin to add
an independent capability that can work with one or more agents. See
[Fork an existing agent](kit-examples.md#fork-an-existing-agent) for an example
that changes Claude Code's permission mode.
## Using kits
Kits can be loaded from a local path (a directory or ZIP file), a Git
repository, or an OCI registry. To launch a sandbox kit, pass its reference in
place of a built-in agent name to `sbx run` or `sbx create`. Use `--kit` for
mixins, and repeat the flag to apply multiple mixins to the same sandbox.
Starting with Docker Sandboxes version 0.42.0, pass the sandbox kit reference
as the first argument:
```console
$ sbx run <sandbox-kit-ref> [PATH...]
$ sbx create <sandbox-kit-ref> [PATH...]
```
The previous form, `sbx run <sandbox-kit-name> --kit <sandbox-kit-ref>`, is
deprecated.
> [!IMPORTANT]
> A mixin passed with `--kit` only takes effect when a sandbox is created.
> Passing it against an
> existing sandbox name fails with
> `--kit can only be used when creating a new sandbox`. To add a supported
> mixin kit to a running sandbox, use [`sbx kit add`](#local) instead.
> `sbx kit add` restarts the sandbox to apply the updated kit set.
> VM state — installed packages, Docker images, volumes, and agent history
> — is preserved across the restart. It supports mixin kits limited to
> `environment.variables`, `setup.install`, and `permissions.network.allow`.
> To use other fields, recreate the sandbox with the mixin.
### Pass arguments to kits
A schema v2 kit can declare inputs in a top-level `args:` block and reference
them in `spec.yaml` or static files with `${{ kit.args.<name> }}`. Supply a
value with `--kit-arg name=value`:
```console
$ sbx run ./my-agent/ --kit-arg channel=beta
```
Kit argument values are plain text. Values supplied with `--kit-arg` can remain
in your shell history, and argument files store their values unencrypted. Don't
use kit arguments for secrets. Use [Credentials](../configuration/credentials.md)
instead.
An argument without a kit name prefix applies to every kit that declares it.
To target one kit, prefix the argument with the value of that kit's `name`
field and a period:
```console
$ sbx run ./my-agent/ \
--kit ./my-mixin/ \
--kit-arg version=1.2.3 \
--kit-arg my-mixin.version=2.0.0
```
The kit-specific value takes precedence over the shared value for `my-mixin`.
Use `--kit-args-file` for a reusable set of `name=value` entries. Blank lines
and lines that start with `#` are ignored:
```text {title="kit.args"}
version=1.2.3
my-mixin.channel=beta
```
```console
$ sbx create ./my-agent/ . \
--kit ./my-mixin/ \
--kit-args-file ./kit.args \
--kit-arg my-mixin.channel=stable
```
When you pass multiple argument files, a value in a later file overrides the
same key in an earlier file. Values passed with `--kit-arg` override every
file. For repeated `--kit-arg` entries with the same key, the last value wins.
Argument validation happens before the sandbox is created. `sbx` rejects a
missing required value, a value outside its declared `enum` or `pattern`, a
placeholder without a declaration, and a supplied argument that no resolved
kit declares. Pass the same argument flags to `sbx kit validate` or
`sbx kit inspect` when the kit requires values. See
[Kit arguments](kit-reference.md#arguments) for the declaration fields.
### Local
Launch a local sandbox kit by passing its directory or ZIP file in place of the
agent name. Relative paths must start with `./` or `../` so `sbx` can
distinguish them from agent and sandbox names:
```console
$ sbx run ./my-agent/
$ sbx create ../my-agent-1.0.zip .
```
Pass a local mixin with `--kit`:
```console
$ sbx run claude --kit ./my-mixin/
$ sbx run claude --kit ../my-mixin-1.0.zip
```
While iterating on a supported mixin kit, apply changes to a running sandbox
with `sbx kit add`:
```console
$ sbx kit add my-sandbox ./my-kit/
```
`sbx kit add` restarts the sandbox to apply the updated kit set.
VM state — installed packages, Docker images, volumes, and agent history — is
preserved across the restart. Kits can't be removed from a running sandbox —
remove and recreate it to start clean.
### Git repository
Launch a sandbox kit from a Git repository:
```console
$ sbx run "git+https://github.com/docker/sbx-kits-contrib.git#ref=v0.1.0&dir=amp"
```
Pass a Git-hosted mixin with `--kit`:
```console
$ sbx run claude --kit "git+https://github.com/docker/sbx-kits-contrib.git#ref=v0.1.0&dir=code-server"
```
- `#ref=<branch|tag|commit>` pins to a specific revision. Defaults to the
repository's default branch.
- `#dir=<path>` loads a kit from a subdirectory.
- `git+ssh://` URLs also work, using your local SSH agent, Git credential
helpers, and `.netrc`.
- Quote the URL in shells where `&` starts a background job.
### OCI registry
Launch a sandbox kit from an OCI registry:
```console
$ sbx run docker.io/sbx/droid-kit:latest
```
Pass an OCI-hosted mixin with `--kit`:
```console
$ sbx run claude --kit ghcr.io/myorg/my-kit:1.0
```
For Docker Hub, include the full `docker.io` prefix. See
[Packaging and distribution](#packaging-and-distribution) for publishing.
> [!IMPORTANT]
> For Docker Hub, `sbx` reuses your `sbx login` session to pull private
> kits. For other registries, store pull credentials with
> [`sbx secret set --registry`](../configuration/credentials.md#registry-credentials)
> before running the sandbox. These credentials take priority over credentials
> in the Docker credential store:
>
> ```console
> $ gh auth token | sbx secret set --registry ghcr.io --password-stdin
> ```
>
> Without credentials from either store, pulls from non-Docker Hub registries
> are anonymous and private kits fail to pull.
### Restrict kit sources
`sbx` restricts which sources a kit can install from. A kit's install
commands run with root privileges inside the sandbox, so limiting where kits
come from reduces supply-chain risk. By default, only kits hosted on Docker
Hub (`docker.io/`) are allowed. Loading a kit from any other source fails:
```console
$ sbx run claude --kit "git+https://github.com/docker/sbx-kits-contrib.git#dir=vale"
ERROR: resolve kits: kit "git+https://github.com/docker/sbx-kits-contrib.git#dir=vale" cannot be installed — its source is not in your allowlist.
```
To allow another publisher, add its host or host/path prefix to the
[`kit.allowedSources`](../configuration/settings.md#kitallowedsources) setting.
The setting replaces the whole list, so include the entries you want to keep:
```console
$ sbx settings set kit.allowedSources '["docker.io/","github.com/docker/"]'
```
Entries match as prefixes on a path-segment boundary, so `github.com/docker/`
allows `github.com/docker/sbx-kits-contrib` but not `github.com/docker-evil/kit`.
To remove the restriction and allow any remote source, set the list to
`["*"]`. This isn't recommended.
Installing from a local directory or ZIP file is governed separately by the
`kit.allowLocalKits` setting, which defaults to `true`. Set it to `false` to
require a remote source:
```console
$ sbx settings set kit.allowLocalKits false
```
For defaults and environment variable equivalents, see the
[kit settings reference](../configuration/settings.md#kits).
## Sign and verify kits
Use cosign-compatible Sigstore signatures to verify who approved a kit and
that its signed content hasn't changed. Signing is keyless by default. Verify a
keyless signature with the certificate identity and OpenID Connect (OIDC)
issuer:
```console
$ sbx kit sign ./my-kit/
$ sbx kit verify \
--certificate-identity user@example.com \
--certificate-oidc-issuer https://accounts.google.com \
./my-kit/
```
For key-based signing, use an ECDSA P-256 key pair:
```console
$ sbx kit sign --key cosign.key ./my-kit/
$ sbx kit verify --key cosign.pub ./my-kit/
```
For a local directory, `sbx kit sign` writes a `kit.sig.bundle` file next to
`spec.yaml`. Commit this file so consumers can verify a kit loaded from the Git
repository. For an OCI kit, the signature is stored as an OCI referrer. You can
sign an OCI kit after pushing it, or push and sign it in one step:
```console
$ sbx kit push ./my-kit/ ghcr.io/myorg/my-kit:1.0 --sign
```
ZIP kits can't carry verifiable signatures.
### Require signed kits
Set [`kit.trustedSigners`](../configuration/settings.md#kittrustedsigners) to
the identities or keys you trust before requiring signatures. Otherwise, `sbx`
uses the default policy, which trusts
Docker employee identities attested by Google's OpenID Connect issuer. A
keyless policy must specify both the certificate identity and its OpenID
Connect issuer:
```console
$ sbx settings set kit.trustedSigners \
'[{"identity":"release-bot@example.com","issuer":"https://accounts.google.com"}]'
$ sbx settings set kit.requireSignature true
```
To trust a key-based signature, set the policy to the public key path:
```console
$ sbx settings set kit.trustedSigners '[{"key":"/path/to/cosign.pub"}]'
$ sbx settings set kit.requireSignature true
```
When `kit.requireSignature` is `true`, `sbx` rejects unsigned kits, signatures
that don't match `kit.trustedSigners`, and ZIP kits. This policy applies when a
kit is loaded from a local directory, Git repository, or OCI registry.
The signature covers `spec.yaml` and the kit's `files/` content, but not mutable
dependencies such as image tags or content downloaded by install and startup
commands. Pin those dependencies by digest or checksum when they must remain
immutable.
## Packaging and distribution
The `sbx kit` subcommands validate, inspect, and publish kits:
- `sbx kit validate <path>` — check that a kit directory or ZIP is
well-formed.
- `sbx kit inspect <path>` — display kit details. Add `--json` for
machine-readable output.
- `sbx kit pack <path> -o <file.zip>` — package a directory as a ZIP file
for sharing.
- `sbx kit push <path> <ref>` — publish to an OCI registry (for example,
`ghcr.io/myorg/my-kit:1.0`).
- `sbx kit pull <ref>` — download a kit from a registry as a ZIP file to
the working directory.
For Docker Hub, include the full `docker.io` prefix — `sbx` doesn't add it
automatically.
For Docker Hub, `sbx kit pull` and `sbx kit push` use the session from
`sbx login`. For other registries, they prefer credentials stored with
[`sbx secret set --registry`](../configuration/credentials.md#registry-credentials).
Both commands fall back to the Docker credential store, so credentials from
`docker login` also work.
## Spec reference
For a field-by-field reference of every `spec.yaml` block — top-level
fields, arguments, credentials, network, environment, setup, static files,
agent instructions, and the sandbox block — see [Kit spec reference](kit-reference.md).
## Debugging
When a kit doesn't behave as expected, start with the network policy log
and direct inspection inside the sandbox:
- `sbx policy log` shows every outbound request the sandbox proxy saw,
the rule it matched, extra context when available, and its `PROXY`
value, such as `forward`, `forward-bypass`, `transparent`, or
`browser-open`. Use it to diagnose install-time download failures,
blocked domains, and unexpected TLS interception. If downloads fail or
arrive corrupted after you add a credential's `apiKey.inject`, check
whether an injection domain is too broad. Inject only on the hosts that
need credentials.
- `sbx exec <sandbox> -- <cmd>` runs an arbitrary command inside an
existing sandbox. Useful for inspecting post-install state without
recreating: `which mytool`, `ls /home/agent/.local/bin/`,
`cat /home/agent/.config/...`, and so on.
Install and startup command output is only emitted during `sbx run` or
`sbx create`; `sbx` doesn't retain it for later inspection. To repeat
setup with fresh output, remove and recreate the sandbox:
`sbx rm <sandbox> && sbx run ...`.

View File

@@ -1,270 +0,0 @@
---
title: Templates
weight: 10
description: Build reusable sandbox images with tools and configuration baked in, or save a running sandbox as a template.
keywords: sandboxes, sbx, templates, images, dockerfile, snapshot, custom environments
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
Every sandbox is customizable — agents install packages, pull images, and
configure tools as they work, and those changes persist for the sandbox's
lifetime. Templates capture a configured environment into a reusable image
so you don't have to set it up again every time.
## Custom templates
Custom templates are reusable sandbox images that extend one of the built-in
agent environments with additional tools and configuration baked in. Instead
of asking the agent to install packages every time, build a template once and
reuse it across sandboxes and team members.
Templates make sense when multiple people need the same environment, when
setup involves steps that are tedious to repeat, or when you need pinned
versions of specific tools. For one-off work, the default image is fine —
ask the agent to install what's needed.
> [!NOTE]
> Custom templates customize an existing agent's environment — they don't
> create new agent runtimes. The agent that launches inside the sandbox is
> determined by the base image variant you extend and the agent you specify
> in the `sbx run` command, not by binaries installed in the template. To
> define a new agent from scratch, see [Kits](kits.md#define-an-agent).
### Base images
All sandbox templates are published as
`docker/sandbox-templates:<variant>`. They are based on Ubuntu and run as a
non-root `agent` user with sudo access. Most variants include Git, Docker
CLI, and common development tools like Node.js, Python, Go, and Java.
| Variant | Agent |
| --------------------- | -------------------------------------------------------------------- |
| `claude-code` | [Claude Code](https://claude.ai/download) |
| `claude-code-minimal` | Claude Code with a minimal toolset (no Node.js, Python, Go, or Java) |
| `codex` | [OpenAI Codex](https://github.com/openai/codex) |
| `copilot` | [GitHub Copilot](https://github.com/github/copilot-cli) |
| `cursor-agent` | [Cursor](https://cursor.com/cli) |
| `devin` | [Devin CLI](https://docs.devin.ai/work-with-devin/devin-cli) |
| `docker-agent` | [Docker Agent](https://github.com/docker/docker-agent) |
| `droid` | [Droid](https://www.factory.ai) |
| `gemini` | [Gemini CLI](https://github.com/google-gemini/gemini-cli) |
| `kiro` | [Kiro](https://kiro.dev) |
| `opencode` | [OpenCode](https://opencode.ai) |
| `shell` | No agent pre-installed. Use for manual agent setup. |
Each variant also has a `-docker` version (for example, `claude-code-docker`)
that includes a full Docker Engine running inside the sandbox — no local Docker
daemon required. When you pick a built-in agent without specifying a custom
template, `sbx run` and `sbx create` use the `-docker` template variants by
default.
The agent containers created from the `-docker` templates run in privileged
mode inside the microVM (not on your host), with a dedicated block volume at
`/var/lib/docker`, and `dockerd` starts automatically inside the sandbox. The
block volume defaults to 10 GB and uses a sparse file, so it only consumes
disk space as Docker writes to it.
To change the volume size for a sandbox, set
`DOCKER_SANDBOXES_DOCKER_SIZE` when you create it:
```console
$ DOCKER_SANDBOXES_DOCKER_SIZE=20g sbx run claude
```
The volume size must be at least 512 MiB. The environment variable doesn't
resize existing volumes.
Use the non-Docker variant if you don't need to build or run containers
inside the sandbox and want a lighter, non-privileged environment. Specify
it explicitly with `--template`:
```console
$ sbx run claude --template docker.io/docker/sandbox-templates:claude-code
```
### Build a custom template
Building a custom template requires
[Docker Desktop](/manuals/desktop/_index.md).
Write a Dockerfile that extends one of the base images. Pick the variant
that matches the agent you plan to run. For example, extend `claude-code`
to customize a Claude Code environment, or `codex` to customize an OpenAI
Codex environment.
The following example creates a Claude Code template with Rust and
protocol buffer tools pre-installed:
```dockerfile
FROM docker/sandbox-templates:claude-code
USER root
RUN apt-get update && apt-get install -y protobuf-compiler
USER agent
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
```
Use `root` for system-level package installations (`apt-get`), and switch
back to `agent` before installing user-level tools. Tools that install into
the home directory, such as `rustup`, `nvm`, or `pyenv`, must run as
`agent` — otherwise they install under `/root/` and aren't available in
the sandbox.
Build the image and push it to an OCI registry, such as Docker Hub:
```console
$ docker build -t my-org/my-template:v1 --push .
```
> [!NOTE]
> The Docker daemon used by Docker Sandboxes pulls templates from a
> registry directly; it doesn't share the image store of your local Docker
> daemon on the host. To route Docker Hub image pulls through your
> organization's registry infrastructure, configure a
> [registry mirror](../configuration/registry-mirror.md).
> [!IMPORTANT]
> For Docker Hub, `sbx` reuses your `sbx login` session to pull private
> images. For other registries (GitHub Container Registry, ECR, ACR, a
> self-hosted Nexus, and so on), store pull credentials with
> [`sbx secret set --registry`](../configuration/credentials.md#registry-credentials)
> before running the sandbox:
>
> ```console
> $ gh auth token | sbx secret set --registry ghcr.io --password-stdin
> ```
>
> Without stored credentials, pulls from non-Docker Hub registries are
> anonymous and private images fail to pull.
For locally-built images, save the image to a tar and load it directly
into the sandbox runtime instead of pulling from a registry:
```console
$ docker image save my-org/my-template:v1 -o my-template.tar
$ sbx template load my-template.tar
$ sbx run --template my-org/my-template:v1 claude
```
`sbx template load` imports the tar into the sandbox runtime's image
store, so the image doesn't need to be reachable from a registry at
sandbox creation time.
Unless you use the permissive `allow-all` network policy, you may also need
to allow-list any domains that your custom tools depend on:
```console
$ sbx policy allow network "*.example.com:443,example.com:443"
```
Then run a sandbox with your template. The agent you specify must match
the base image variant your template extends:
```console
$ sbx run --template docker.io/my-org/my-template:v1 claude
```
Because this template extends the `claude-code` base image, you run it
with `claude`. If you extend `codex`, use `codex`; if you extend `shell`,
use `shell` (which drops you into a Bash shell with no agent).
> [!NOTE]
> Unlike Docker commands, `sbx` does not automatically resolve the Docker
> Hub domain (`docker.io`) in image references.
### Template caching
When creating a sandbox, `sbx` checks the registry for the template image by
default and downloads missing or updated layers. If the pull fails and the
image is cached locally, it can use the cached image. Cached images persist
across sandbox creation and deletion, and are cleared when you run `sbx reset`.
### Updating agents
Agent templates include an agent version, which can differ from the agent's
latest release. Updating the `sbx` CLI or pulling an updated template doesn't
update agents inside existing sandboxes.
To update an installed agent, run its documented update command inside the
sandbox, either from a sandbox shell or with `sbx exec`. Restart the agent
session to use the updated version. The update persists across sandbox stops
and starts, but is deleted when you remove the sandbox. To reuse the updated
agent in other sandboxes, [save a template](#saving-a-sandbox-as-a-template).
## Saving a sandbox as a template
Instead of writing a Dockerfile, you can save a running sandbox's state as
a template. This captures installed packages, configuration changes, and
files into a reusable image — useful when you've set up an environment
interactively and want to preserve it.
> [!WARNING]
> Saving a sandbox captures its entire filesystem, including any secrets
> stored on it. If you manually added API keys, tokens, or other
> credentials to the sandbox, they're embedded in the saved template and
> shared with anyone you distribute it to. To keep credentials out of
> templates, manage them with `sbx secret set` instead — the proxy injects
> them at runtime so they're never written to the filesystem. For more
> information, see [Manage credentials](../configuration/credentials.md).
### Save and reuse
Stop the sandbox (or let the CLI prompt you), then save it with a name and
tag:
```console
$ sbx template save my-sandbox my-template:v1
```
The image is stored in the sandbox runtime's local image store. Create a
new sandbox from it with the `-t` flag:
```console
$ sbx run -t my-template:v1 claude
```
### List and remove templates
List all saved templates:
```console
$ sbx template ls
```
Remove a template you no longer need:
```console
$ sbx template rm my-template:v1
```
### Export and import
To share a saved template or move it to another machine, export it as a
tar file:
```console
$ sbx template save my-sandbox my-template:v1 --output my-template.tar
```
On the other machine, load the tar file and use it:
```console
$ sbx template load my-template.tar
$ sbx run -t my-template:v1 claude
```
### Limitations
Agent configuration files are always recreated when a sandbox is created.
Changes to user-level agent configuration files, such as
`/home/agent/.claude/settings.json` and `/home/agent/.claude.json`, do not
persist in saved templates.
If the saved template was built for a different agent than the one you
specify in `sbx run`, you get a warning. For example, saving a Claude
sandbox and running it with `codex` produces:
```text
⚠ WARNING: template "my-template:v1" was built for the "claude" agent but you are using "codex".
The sandbox may not work correctly. Consider using: sbx run -t my-template:v1 claude
```

View File

@@ -0,0 +1,302 @@
---
title: Use kits
description: Run published v3 kit sets and workloads, add compatible mixins, and configure kit arguments and runtime access.
keywords: sandboxes, sbx, kits, v3, workloads, mixins
weight: 10
---
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
If you've run `sbx run claude` or `sbx run codex`, you've already used a kit.
The built-in agents are kits that package an environment, tools, and runtime
settings. You run kits you build yourself or get from another publisher in the
same way: give `sbx` a reference, and Docker Sandboxes prepares the environment
and applies the kit's settings.
This page shows how to run published kits, combine them with mixins, and
customize their settings.
## Run a kit
The built-in agent names are shortcuts for kit references. To run another kit,
replace the agent name with that kit's reference. For example, run Docker's
published v3 Codex workload:
```console
$ sbx run docker.io/docker/sbx-kit-codex:0.155.1 --name codex-v3-kit
```
This starts Codex using a v3 kit from Docker Hub. The built-in `codex`
shortcut uses a v2 kit to preserve compatibility with existing customizations.
### Choose a kit source
The previous example uses a published image from Docker Hub. You can also
run a kit from a local directory or a Git repository. The same source types
work for mixins added with `--kit`:
| Source | Example reference |
| --- | --- |
| Published image | `docker.io/my-org/agent-kit:1.0.0` |
| Local directory | `./my-agent` |
| Git repository | `git+https://github.com/<ORG>/<REPOSITORY>.git#ref=<COMMIT>&dir=my-agent` |
For Git sources, `ref` selects a revision and `dir` selects the kit's
subdirectory. Quote Git URLs in shell commands because they can contain `&`:
```console
$ sbx run "git+https://github.com/<ORG>/<REPOSITORY>.git#ref=<COMMIT>&dir=my-agent"
```
`sbx` pulls published images and builds local or Git sources when creating
the sandbox. Builds with unchanged source content and supplied kit arguments
reuse cached results.
By default, remote kit sources are limited to Docker Hub. To use a Git
source or another registry, see [Restrict kit sources](#restrict-kit-sources).
For private images, see
[Registry credentials](/manuals/ai/sandboxes/configuration/credentials.md#registry-credentials).
## Reuse a sandbox
A sandbox keeps the kit configuration it was created with. To return to the
sandbox from the previous example, specify its name:
```console
$ sbx run --name codex-v3-kit
```
You don't need to specify the kit reference again. To try a different workload,
mixin combination, or argument value, create a sandbox with a different name
or recreate the existing one. To add a mixin to an existing sandbox, see
[Add mixins](#add-mixins).
## Runtime access and instructions
A kit can request access to the services it needs. For example, the Codex
workload needs to reach OpenAI and authenticate. If you use an API key,
store it on your host and approve the kit's request to use it. These are
separate steps: storing a secret doesn't give a third-party kit permission to
use it. See
[Credential bindings](/manuals/ai/sandboxes/configuration/credentials.md#credential-bindings)
for preparing unattended runs.
Check the kit's documentation for the services it contacts, the credentials
it needs, and the commands it runs at startup. For proxy-managed credentials,
the kit's credential binding tells the proxy which credential to add to
requests to the service. Once you approve the binding, the proxy adds the
credential to outgoing requests. The credential value stays on your host
and isn't exposed inside the sandbox.
Network requests must also meet your sandbox's network policy. Kit allow rules
can't grant access beyond your organization's policy. If a connection fails,
check the [policy log](#debug-kits) to see which rule blocked it.
Kits can also give the agent instructions for using their tools. The workload
chooses the instruction file, and mixins add their guidance to it. Docker
Sandboxes writes that file outside your workspace, leaving your project's
instructions intact.
## Add mixins
Mixins add tools and configuration to a workload. Use `--kit` to add a mixin
when creating a sandbox. For v3 kits, both the workload and the mixin must
use v3.
For example, an internal CLI mixin can package your company's executable
along with network rules and a credential request for its API:
```console
$ sbx run docker.io/docker/sbx-kit-codex:0.155.1 --name codex-tools \
--kit docker.io/<NAMESPACE>/company-cli:1.0.0
```
Replace the mixin reference with one your organization has published.
To build your own, see
[Build a tool mixin](/manuals/ai/sandboxes/customize/author/tool-mixins.md).
Store the credential requested by the mixin on the host and approve access
when prompted.
Codex starts with `company-cli` available to use. Docker Sandboxes
applies the files and settings from both the workload and the mixin.
Built-in shortcuts such as `claude` and `codex` use v2 kits and require
[v2 mixins](/manuals/ai/sandboxes/customize/kits-v2.md).
See [Version compatibility](/manuals/ai/sandboxes/customize/_index.md#version-compatibility)
for details.
### Combine multiple mixins
Repeat `--kit` to add more mixins. You can also add compatible mixins to a
published workload set, or pass a set containing only mixins with `--kit`.
Avoid adding a kit that's already in the set. If two kits provide the same
feature, Docker Sandboxes can reject the combination.
To publish your combination as one reference, see
[Compose a kit set](/manuals/ai/sandboxes/customize/author/kit-sets.md).
### Change a sandbox's mixins
For v3 kits, you choose the mixins when you create the sandbox. To use a
different combination, create another sandbox with a different name.
Specify the workload and all the mixins you want to include.
The `sbx kit add` command can't add mixins to an existing v3 sandbox.
The new sandbox doesn't inherit changes or kit volume data from the previous sandbox.
## Compose kits
Some mixins need a tool another kit supplies. For example, a mixin whose
scripts call `gojq` can declare that dependency:
```yaml
requires: [gojq]
```
The [gojq mixin example](/manuals/ai/sandboxes/customize/author/patterns.md#leave-build-tools-out-of-the-mixin)
declares the feature it provides:
```yaml
provides: ["gojq@0.12.17"]
```
Include both mixins when you create the sandbox. Docker Sandboxes applies
the gojq mixin before the mixin whose scripts need it, regardless of the order
of your `--kit` flags. If you leave out gojq, sandbox creation fails.
A `requires` entry tells Docker Sandboxes what a kit needs. You still choose
and include the kit that provides it. Docker Sandboxes doesn't download a
kit for you based on this entry. A kit can also require a minimum feature
version or rule out incompatible kits.
With `--kit`, Docker Sandboxes checks and combines the kits when it creates
the sandbox. For a set, this happens when the publisher builds it. The
published kit includes a record of the exact components used, identified by
their image digests.
To declare these relationships in your own kits, see
[Composition fields](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/SPEC-v3.md#5-provides-requires-integrates-conflicts).
## Pass arguments to kits
Kits can expose arguments for settings such as a linter's mode. Check the
kit's documentation for argument names, defaults, and accepted values.
Use `--kit-arg name=value` to set an argument:
```console
$ sbx run docker.io/<NAMESPACE>/codex-tools:1.0.0 \
--name codex-tools-fix --kit-arg lint_mode=fix
```
This example uses a workload set that exposes a `lint_mode` argument.
Replace the image reference with your set's published reference. To expose
arguments in your own set, see
[Configure component arguments](/manuals/ai/sandboxes/customize/author/kit-sets.md#configure-component-arguments).
Argument values are plain text and can be recorded in shell history and
sandbox state. Use
[stored credentials](/manuals/ai/sandboxes/configuration/credentials.md)
for secrets.
### Target a specific kit
An argument without a kit prefix applies to every selected kit that declares
it. To target one kit, use `--kit-arg <HANDLE>.<ARGUMENT>=<VALUE>`:
```console
$ sbx run docker.io/<NAMESPACE>/codex-tools:1.0.0 \
--name codex-tools-fix --kit-arg codex-tools.lint_mode=fix
```
The handle identifies the kit and comes from its reference:
| Kit source | Handle |
| --- | --- |
| Published image | Final part of the repository name, such as `codex-tools` |
| Local directory | Directory name |
| Git repository | Selected subdirectory name, or repository name if no subdirectory is selected |
A value targeted at one kit overrides a value supplied to all kits.
### Load arguments from a file
Use `--kit-args-file <FILE>` to load arguments from a reusable file.
Write one `name=value` entry per line. You can prefix names with a kit
handle, as with `--kit-arg`.
Values passed with `--kit-arg` override those in the file.
### Which settings can you change?
For a kit set, you can change only the arguments the author has exposed.
Other component arguments are fixed when the set is published.
Settings chosen during the image build, such as a tool's version, require
rebuilding the image.
## Restrict kit sources
[`kit.allowedSources`](../configuration/settings.md#kitallowedsources) controls
permitted remote kit sources. Its default permits
Docker Hub. To include a Git publisher, set the complete list of prefixes you
want to permit:
```console
$ sbx settings set kit.allowedSources '["docker.io/","github.com/docker/"]'
```
Prefixes match at path-segment boundaries. Local source directories are
controlled separately by `kit.allowLocalKits`, which defaults to `true`:
```console
$ sbx settings set kit.allowLocalKits false
```
For defaults and environment variable equivalents, see the
[kit settings reference](../configuration/settings.md#kits).
## Verify kit signatures
To verify a published kit against its author's signing identity:
```console
$ sbx kit verify docker.io/<NAMESPACE>/my-kit:1.0.0 \
--certificate-identity <SIGNER_IDENTITY> \
--certificate-oidc-issuer <ISSUER_URL>
```
For key-based signatures, use `--key cosign.pub` instead of the certificate
identity and issuer options. Obtain the expected identity or public key from
the publisher.
To require trusted signatures when loading kits, configure the trusted signer
policy, then turn on the requirement:
```console
$ sbx settings set kit.trustedSigners \
'[{"identity":"release-bot@example.com","issuer":"https://accounts.google.com"}]'
$ sbx settings set kit.requireSignature true
```
V3 source directories and Git sources don't support the source-signing
workflow. Use a signed OCI image when signatures are required.
## Debug kits
When a tool is missing or a request fails, inspect the running sandbox:
```console
$ sbx exec <SANDBOX> -- which <TOOL>
$ sbx exec <SANDBOX> -- cat /home/agent/.config/<TOOL>/settings.json
$ sbx policy log
```
The policy log shows outbound requests and the rules they matched. Use it to
find blocked package registries or API hosts. If downloads fail after adding a kit that requests
credentials, ask its author to check that credential injection targets only
the service hosts that need it.
To try an updated v3 kit, create a sandbox with a different name. Reusing
an existing sandbox keeps its recorded kit configuration.

View File

@@ -114,23 +114,13 @@ inside the session. Most agents let you switch permission modes after
startup. In Claude Code, use the `/permissions` command to change the mode
interactively.
To make approval prompts the default for every session, define a custom
sandbox kit that overrides the agent's entrypoint to drop the
permission-skipping flag. For example, a kit that launches Claude Code
without `--dangerously-skip-permissions`:
To make approval prompts the default for every session, create a v2 sandbox
kit that extends the built-in agent and changes its launch options. See
[Fork an existing agent](customize/kits-v2.md#fork-an-existing-agent)
for a complete example.
```yaml {title="claude-safe/spec.yaml"}
schemaVersion: "1"
kind: sandbox
name: claude-safe
sandbox:
image: "docker/sandbox-templates:claude-code-docker"
entrypoint:
run: [claude]
```
Run it with `sbx run ./claude-safe/`. See
[Sandbox kits](customize/kits.md#sandbox-kits) for the full pattern.
For an environment built entirely with v3 kits, set the launch command in
the workload's Dockerfile. See [Build a v3 agent kit](/manuals/ai/sandboxes/customize/author/build-an-agent.md).
## How do I know if my agent is running in a sandbox?

View File

@@ -242,8 +242,7 @@ from those sources do still apply. See [Precedence](#precedence).
What applies depends on whether your organization has governance enabled:
- No organization governance: local rules and any
[kit-defined network rules](../customize/kits.md#control-network-access)
- No organization governance: local rules and any kit-defined network rules
determine what sandboxes can access.
- Organization governance active: organization policy determines what access can
be granted. Only organization allow rules grant access, so local and
@@ -251,6 +250,10 @@ What applies depends on whether your organization has governance enabled:
permits. Deny rules apply from every source, so a local or kit-defined deny
can still restrict access further.
For kit-defined rules, see
[Network policies](https://github.com/docker/sandbox-kit-spec/blob/main/docs/spec/capabilities/com.docker.sandbox/network-policy@1.md)
in the kit specification.
Precedence is decided by a rule's decision rather than its source:
| Rule | Evaluated under organization governance |

View File

@@ -29,7 +29,7 @@ The columns are:
- `POLICY`: the policy name.
- `SOURCE`: where the policy came from. `local` means your local configuration
— a preset or rules you added with `sbx policy`. `kit` means a
[kit](../../customize/kits.md#control-network-access). `org` means your
[kit](/manuals/ai/sandboxes/governance/concepts.md#precedence). `org` means your
organization.
- `APPLIES TO`: which sandboxes the policy applies to. `all` means the policy
is global. `sandbox:<name>` scopes it to a single sandbox; a profile name

View File

@@ -49,8 +49,8 @@ $ sbx exec <sandbox> -- sh -lc 'command -v g++ && command -v make && command -v
A manual install lasts only until the sandbox is recreated, and the first
connection still builds `node-pty` from source. For a setup that persists,
recreate the sandbox with the [kit](../customize/kits.md) or a custom
[template](../customize/templates.md).
recreate the sandbox with the [v2 kit](../customize/kits-v2.md) or a custom
[template](/manuals/ai/sandboxes/customize/author/base-images.md).
## Connect

View File

@@ -119,7 +119,7 @@ see the full list of active rules, and remove entries you don't need. See
Kits run install commands with root privileges inside the sandbox. To limit
supply-chain risk, `sbx` restricts kit installs to an allowlist of sources
that defaults to Docker Hub only. See
[Restrict kit sources](../customize/kits.md#restrict-kit-sources).
[Restrict kit sources](/manuals/ai/sandboxes/customize/use-kits.md#restrict-kit-sources).
Shared agent skills create a narrow exception to cross-sandbox isolation. The
store can be mounted with `readwrite` access, so one sandbox can modify

View File

@@ -136,7 +136,7 @@ $ sbx settings set kit.allowedSources '["docker.io/","github.com/docker/"]'
```
Then run the command again. For details, including how to allow local kits or
any remote source, see [Restrict kit sources](customize/kits.md#restrict-kit-sources).
any remote source, see [Restrict kit sources](/manuals/ai/sandboxes/customize/use-kits.md#restrict-kit-sources).
## SSH and other non-HTTP connections fail
@@ -220,9 +220,10 @@ your organization's internal root CA inside the sandbox so the agent and its
SDKs trust certificates signed by the proxy. Certificate errors can stop a
request before the credential proxy can inject credentials.
For repeatable setup, create a [sandbox kit](customize/kits.md) that installs
the CA when the sandbox is created. See
[Install an internal CA certificate](customize/kit-examples.md#install-an-internal-ca-certificate)
For repeatable setup with a built-in agent, create a
[v2 mixin kit](customize/kits-v2.md) that installs the CA when the
sandbox is created. See
[Install an internal CA certificate](customize/kits-v2.md#install-an-internal-ca-certificate)
for an example kit.
Use a PEM-encoded certificate with a `.crt` extension. If traffic can be signed

View File

@@ -2,7 +2,7 @@
title: Usage
weight: 30
description: Basic sbx commands for creating, managing, and connecting to Docker Sandboxes.
keywords: docker sandboxes, sbx, usage, run, create, stop, remove, ports, workspaces
keywords: docker sandboxes, sbx, usage, run, create, stop, remove, ports, workspaces, templates, save, load
---
Use this page as a command-oriented guide to day-to-day `sbx` operations. For
@@ -397,5 +397,154 @@ changes, command history, and mountless workspace files all persist across
stops and restarts. When you remove a sandbox, everything inside is deleted.
Host workspace files, including repositories used as clone sources, and the
[shared agent skills store](workflows/agent-skills.md) remain on your host. To
preserve a configured environment, create a [custom
template](customize/templates.md) or use a [kit](customize/kits.md).
capture changes in the container filesystem, [save a template](#saving-a-sandbox-as-a-template).
For a reproducible environment defined in source,
[author a kit](/manuals/ai/sandboxes/customize/author/_index.md).
## Saving a sandbox as a template
Save a sandbox's container filesystem as a reusable template image after
setting up tools or configuration interactively. A template contains image
content; the agent kit still supplies runtime settings such as credentials
and network rules. The examples here reuse templates with built-in agents.
A saved template isn't a backup of the whole sandbox. Mounted filesystems,
including host workspaces and the Docker store at `/var/lib/docker`, aren't
included. Save any data from those mounts separately.
> [!WARNING]
> Saving a sandbox captures files in its container filesystem, including any
> secrets stored there. If you manually added API keys, tokens, or other
> credentials to the sandbox, they're embedded in the saved template and
> shared with anyone you distribute it to. To keep credentials out of
> templates, manage them with `sbx secret set` instead — the proxy injects
> them at runtime so they're never written to the filesystem. For more
> information, see [Manage credentials](configuration/credentials.md).
### Save and reuse
Stop the sandbox (or let the CLI prompt you), then save it with a name and
tag:
```console
$ sbx template save my-sandbox my-template:v1
```
The image is stored in the sandbox runtime's local image store. Create a
new sandbox from it with the `-t` flag:
```console
$ sbx run -t my-template:v1 claude
```
### List and remove templates
List all saved templates:
```console
$ sbx template ls
```
Remove a template you no longer need:
```console
$ sbx template rm my-template:v1
```
### Export and import
To share a saved template or move it to another machine, export it as a
tar file:
```console
$ sbx template save my-sandbox my-template:v1 --output my-template.tar
```
On the other machine, load the tar file and use it:
```console
$ sbx template load my-template.tar
$ sbx run -t my-template:v1 claude
```
### Limitations
Agent configuration files are always recreated when a sandbox is created.
Changes to user-level agent configuration files, such as
`/home/agent/.claude/settings.json` and `/home/agent/.claude.json`, do not
persist in saved templates.
If the saved template was built for a different agent than the one you
specify in `sbx run`, you get a warning. For example, saving a Claude
sandbox and running it with `codex` produces:
```text
⚠ WARNING: template "my-template:v1" was built for the "claude" agent but you are using "codex".
The sandbox may not work correctly. Consider using: sbx run -t my-template:v1 claude
```
## Load a template
To create a sandbox from a template image in a registry, pass its full image
reference to `--template`. Use the agent the image was prepared for:
```console
$ sbx run --template docker.io/my-org/my-template:v1 claude
```
Unlike Docker commands, `sbx` doesn't automatically add the Docker Hub domain
(`docker.io`) to image references. For available images and the built-in agent
workflow, see [Base images](/manuals/ai/sandboxes/customize/author/base-images.md).
> [!NOTE]
> The Docker daemon used by Docker Sandboxes pulls templates from a
> registry directly; it doesn't share the image store of your local Docker
> daemon on the host. To route Docker Hub image pulls through your
> organization's registry infrastructure, configure a
> [registry mirror](configuration/registry-mirror.md).
> [!IMPORTANT]
> For Docker Hub, `sbx` reuses your `sbx login` session to pull private
> images. For other registries (GitHub Container Registry, ECR, ACR, a
> self-hosted Nexus, and so on), store pull credentials with
> [`sbx secret set --registry`](configuration/credentials.md#registry-credentials)
> before running the sandbox:
>
> ```console
> $ gh auth token | sbx secret set --registry ghcr.io --password-stdin
> ```
>
> Without stored credentials, pulls from non-Docker Hub registries are
> anonymous and private images fail to pull.
For locally-built images, save the image to a tar and load it directly
into the sandbox runtime instead of pulling from a registry:
```console
$ docker image save my-org/my-template:v1 -o my-template.tar
$ sbx template load my-template.tar
$ sbx run --template my-org/my-template:v1 claude
```
`sbx template load` imports the tar into the sandbox runtime's image
store, so the image doesn't need to be reachable from a registry at
sandbox creation time.
### Template caching
When creating a sandbox, `sbx` checks the registry for the template image by
default and downloads missing or updated layers. If the pull fails and the
image is cached locally, it can use the cached image. Cached images persist
across sandbox creation and deletion, and are cleared when you run `sbx reset`.
### Updating agents
Agent templates include an agent version, which can differ from the agent's
latest release. Updating the `sbx` CLI or pulling an updated template doesn't
update agents inside existing sandboxes.
To update an installed agent, run its documented update command inside the
sandbox, either from a sandbox shell or with `sbx exec`. Restart the agent
session to use the updated version. The update persists across sandbox stops
and starts, but is deleted when you remove the sandbox. To reuse the updated
agent in other sandboxes, [save a template](#saving-a-sandbox-as-a-template).

View File

@@ -47,7 +47,7 @@ The token is never stored in plaintext inside the sandbox. See
When using Docker Hub, authentication is handled automatically; `sbx` reuses
your existing login session. For other registries, you need to configure
credentials for `sbx` so it can pull private [templates](../customize/templates.md)
credentials for `sbx` so it can pull private [templates](../usage.md#load-a-template)
and kits when creating a sandbox:
```console

View File

@@ -213,8 +213,8 @@ you turned off forwarding or use a fixed SSH agent socket, see
To apply this configuration automatically to every sandbox, use the
[`git-ssh-sign`](https://github.com/docker/sbx-kits-contrib/tree/main/git-ssh-sign)
community kit, which handles all of the above setup. See [Kits](../customize/kits.md)
if you want to package it alongside other sandbox customizations.
community kit, which handles all of the above setup. For using it with the
built-in agents, see [Kits v2](../customize/kits-v2.md).
For troubleshooting, see
[Sandbox commits aren't signed](../troubleshooting.md#sandbox-commits-arent-signed).

View File

@@ -67,6 +67,9 @@ simplifies the model management experience.
coding agent needs to run in a [Docker Sandbox](../../ai/sandboxes/_index.md):
its image, tools, credentials, network rules, and startup commands.
Each kit's Hub page shows the command to run it, in the form
`sbx run <agent> --kit docker.io/<namespace>/<kit-name>`. To learn more about
building and running kits, see [Kits](../../ai/sandboxes/customize/kits.md).
Follow the publisher's instructions for the kit's schema version and role.
A kit that defines a complete environment is the main reference passed to
`sbx run`. Add mixins with `--kit`. Components in one sandbox must use
compatible kit formats. See [Kits](/manuals/ai/sandboxes/customize/_index.md) for
the v3 model and [Kits v2](/manuals/ai/sandboxes/customize/kits-v2.md) for
kits used with built-in agents.

View File

@@ -77,14 +77,17 @@ Extensions](/manuals/extensions/_index.md).
Docker Hub hosts kits for [Docker Sandboxes](/manuals/ai/sandboxes/_index.md),
which run AI coding agents in isolated microVM sandboxes. A kit packages what
an agent needs to run in a sandbox, such as its image, tools, credentials,
network rules, and startup commands. Sandbox kits come in two kinds:
network rules, and startup commands. In the v2 format, kits have two kinds:
- Sandbox: Defines a full agent from scratch, including its image and
entrypoint.
- Mixin: Layers tools, credentials, or configuration onto an existing agent.
To learn more about building and running kits, see
[Kits](/manuals/ai/sandboxes/customize/kits.md).
For these kits, see the [v2 reference](/manuals/ai/sandboxes/customize/kits-v2.md).
The experimental v3 format uses workloads and mixins, and adds sets for
publishing a composition as one kit. See the
[Kits overview](/manuals/ai/sandboxes/customize/_index.md) for the v3 model and
version compatibility.
#### Helm charts

View File

@@ -51,7 +51,7 @@
"product": "Docker Sandboxes",
"title": "Sign and enforce trusted sandbox kits",
"description": "Sign kits with cosign-compatible Sigstore signatures, verify keyless or key-based signatures, and reject kits outside a trusted-signer policy.",
"url": "/ai/sandboxes/customize/kits/#sign-and-verify-kits",
"url": "/ai/sandboxes/customize/kits-v2/#sign-and-verify-kits",
"published": "2026-08-20",
"source_prs": [25791, 25896],
"featured": false