Share feedback
Answers are generated based on the documentation.

Kit examples

Availability: Early Access

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

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.

ruff-lint/
├── spec.yaml
└── files/
    └── workspace/
        └── ruff.toml
ruff-lint/spec.yaml
schemaVersion: "1"
kind: mixin
name: ruff-lint
displayName: Ruff
description: Python linting with shared team config

commands:
  install:
    - command: "uv tool install ruff@latest"
      user: "1000"
ruff-lint/files/workspace/ruff.toml
line-length = 80

[lint]
select = ["E", "F", "I"]

Install a tool at sandbox creation

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

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

Run a background service

commands.startup runs at every sandbox start. For long-running services, background them inside a shell command and redirect output to a log file. Relying on the background: true field alone can leave the service attached to a shell that exits, which silently kills it.

commands:
  startup:
    - command:
        - sh
        - -c
        - nohup my-service --port 8080 > /tmp/my-service.log 2>&1 &
      user: "1000"

The log file is worth the extra flag: if the service exits early, its stderr goes to a parent shell that isn't attached to anything you can read. An empty log file tells you the wrapper ran; a populated one tells you why it failed.

Bake runtime values into a file with initFiles

When a config file needs a value that isn't known until sandbox start — most often the absolute workspace path — use commands.initFiles. The ${WORKDIR} placeholder expands to the primary workspace path when the file is written.

commands:
  initFiles:
    - 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 initFiles 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 in the contrib repo, 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.

docker-review/
├── spec.yaml
└── files/
    └── workspace/
        └── .claude/
            └── skills/
                └── docker-review/
                    └── SKILL.md
docker-review/spec.yaml
schemaVersion: "1"
kind: mixin
name: docker-review
displayName: Dockerfile review skill
description: Ships a Claude Code skill that reviews Dockerfiles
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 for details.

Fork an existing agent

Agent kits (kind: agent) define a full agent from scratch. The most common variant is a fork of a built-in agent — same image and credentials, but a different entrypoint. This example reproduces the built-in claude agent but drops --dangerously-skip-permissions so every tool call prompts for approval:

claude-safe/spec.yaml
schemaVersion: "1"
kind: agent
name: claude-safe
displayName: Claude Code (with approval prompts)
description: Claude Code without --dangerously-skip-permissions

agent:
  image: "docker/sandbox-templates:claude-code-docker"
  aiFilename: CLAUDE.md
  persistence: persistent
  entrypoint:
    run: [claude]

network:
  serviceDomains:
    api.anthropic.com: anthropic
    console.anthropic.com: anthropic
  serviceAuth:
    anthropic:
      headerName: x-api-key
      valueFormat: "%s"
  allowedDomains:
    - "claude.com:443"

credentials:
  sources:
    anthropic:
      env:
        - ANTHROPIC_API_KEY

Launch with the kit's name: as the agent argument to sbx run:

$ sbx run claude-safe --kit ./claude-safe

For a step-by-step walkthrough of building a new agent kit from scratch, see Build an agent.

More examples

These patterns are all drawn from working kits in the sbx-kits-contrib repository, which contains each example as a complete, loadable kit. Use it to study the full shape of a kit, or load one directly:

$ sbx run claude --kit "git+https://github.com/docker/sbx-kits-contrib.git#dir=<kit>"