Guide

Define and share your sandbox environment

  • Beginner
  • 12 min
  • Published
  • Verified

What you'll learn

  • Explain what an sbxenv.yaml file captures and why it beats a list of flags
  • Write an sbxenv.yaml file for your own project, with pinned kit versions
  • Preview, run, and remove a sandbox with the sbx env commands
  • Share the file so a teammate gets the same sandbox, and update it for everyone at once

The terminals below are simulated. Nothing runs on your machine, and each one starts from the state the guide has reached at that point.

A sandbox that fits a real project usually needs more than an agent. Say yours runs Claude Code with the Playwright kit added, publishes the port your dev server listens on, and sets a variable your app expects. On the command line, that’s one flag at a time:

$ cd ~/my-app
$ sbx run sbx/claude --name claude-my-app \
    --kit sbx/playwright \
    --publish 3000:3000 \
    --env NODE_ENV=development

Then a teammate asks, “How do I get the sandbox you’re using?” Your options are to paste that command into a chat message and hope it still works next month, or to walk them through what each flag means. Neither scales past one person, and both turn you into the team’s sandbox expert.

An environment file is the better answer. It’s a small YAML file, named sbxenv.yaml, that describes the sandbox you want: the agent, the kits, the workspace, the ports, the variables. You commit it next to your code. Anyone who clones the project runs one command and gets the same sandbox. If you’ve used Docker Compose, it’s the same idea, applied to sandboxes: your sandbox, as code.

Before you start

You can follow this whole guide in your browser. The terminals on this page are simulations: click through them to see what each command does.

To run it for real on your own machine, you need:

  • The sbx CLI, installed and signed in. Follow Install Docker Sandboxes, then run sbx login.
  • Your model credentials stored on your machine, for example with sbx secret set anthropic. The environment file never holds them.
  • A project. This guide calls it ~/my-app. Swap in your own.

The guide uses kits and assumes you know what a workload and a mixin are. For a refresher, see Customize your sandbox with kits.

Turn the flags into a file

Create sbxenv.yaml in the root of your project. Each part of the command above becomes a line:

# ~/my-app/sbxenv.yaml
schemaVersion: "1"
name: claude-my-app
agent: claude
workspace: .

kits:
  - sbx/claude:2.1.267
  - sbx/playwright:1.61.1

env:
  NODE_ENV: development

ports:
  - sandbox: 3000
    host: 3000

Reading it top to bottom:

  • schemaVersion is the file format version. It’s always "1" for now.
  • name is the sandbox’s name, the same one you passed with --name.
  • agent names the agent the sandbox launches. For a kit like sbx/claude, it’s the kit’s short name, the last part of its reference: claude.
  • workspace: . shares the directory the file sits in, so the project the file lives in is the project the agent works on. A relative path is read from the file’s location, not from wherever you happen to run the command, so this works no matter where a teammate cloned the project.
  • kits lists every kit that goes into the sandbox. That includes the workload, sbx/claude, which you passed as the first argument to sbx run, and the Playwright mixin you added with --kit.
  • env sets environment variables inside the sandbox.
  • ports publishes a port from the sandbox to your machine, so you can open the agent’s dev server in your browser.

You could leave name out, and sbx would make one up from the agent and the directory. Spelling it out means everyone’s sandbox has the same name, wherever they cloned the project, so claude-my-app in a teammate’s question means the same thing on your machine.

Notice what isn’t in the file: your API key. Each person keeps their own credentials on their own machine, the same way they did before. The file says what sandbox to build. It never carries whose keys to use.

Pin the versions

There’s one deliberate difference from the command you typed. On the command line, you wrote sbx/claude with no tag, which means “the latest one”. In the file, each kit has a version tag: :2.1.267 and :1.61.1.

When it’s just you, “latest” is convenient. When it’s a team, it means two people who run the same file a week apart can get different versions of Claude Code or Playwright, and “works on my machine” is back. A pinned tag means everyone gets exactly the same kits until someone changes the file on purpose. You’ll see what that change looks like later in this guide.

To find the current version of a kit, look at its tags on Docker Hub, for example sbx/claude.

Preview before you run

Because a file can ask for a lot, sbx never applies one blindly. Run sbx env plan from the project directory to see everything it would set up, without changing anything:

Host machine

cd ~/my-app
sbx env plan
── LOAD ENVIRONMENT
   reading environment file…
     /home/moby/my-app
   ✓ environment loaded
── ENVIRONMENT PLAN
   claude-my-app

   kits:
+    - sbx/claude:2.1.267
+    - sbx/playwright:1.61.1

   workspace:  (present, not recorded as applied, needs your approval)
     path: /home/moby/my-app

   env:
+    NODE_ENV: development

+  sandbox:
+    name: claude-my-app
+    agent: claude

   ports:
+    - sandbox: 3000
+      protocol: tcp4
+      host: 3000

   Plan: + 4 to add, ~ 0 to change, - 0 to destroy.

The plan is laid out like your file: the same keys, in the same order. What it adds is the margin. A + is something that will be added, a ~ something that will change, and a - something that will be removed. If a teammate’s file ever runs a command on your machine or uses a credential, it shows up here before it happens.

Run it

sbx env run does what sbx run did: it creates the sandbox if it doesn’t exist yet, then attaches you to the agent. The difference is that it shows you the plan first and asks before it does anything:

Host machine

sbx env run
── ENVIRONMENT PLAN
   claude-my-app

   kits:
+    - sbx/claude:2.1.267
+    - sbx/playwright:1.61.1

   env:
+    NODE_ENV: development

   ports:
+    - sandbox: 3000
+      host: 3000

   Plan: + 4 to add, ~ 0 to change, - 0 to destroy.

   Approve this plan? [y/N] y

Creating sandbox claude-my-app...
Publishing 127.0.0.1:3000 -> 3000/tcp4
Connected to Claude in sandbox claude-my-app.

Once you’re in, it’s an ordinary sandbox. The microVM isolation, the network policy, and credential injection all work exactly as they would for sbx run. The file only decided what went in.

Next time, run the same command. Nothing in the file has changed and you’ve already approved it, so sbx skips the question and attaches you straight to the existing sandbox.

The sbx env commands

You’ve now used two of the five commands. Each takes the same file, so you rarely need any flags:

Command Use it to
sbx env plan See what the file would set up. Changes nothing
sbx env run Create the sandbox if needed, then attach to the agent
sbx env create Create the sandbox without attaching, for example in a script
sbx env exec Run one command in the existing sandbox
sbx env rm Remove the sandbox and the credentials scoped to it, after you approve

With no path, each command reads the sbxenv.yaml in your current directory. You can also pass a directory or a file path. For example, here’s how to run your project’s tests inside the sandbox without opening the agent:

Host machine

sbx env exec -- npm test
> my-app@1.0.0 test
> playwright test

Running 12 tests using 4 workers

  12 passed (9.4s)

Everything before -- is for sbx. Everything after it is the command to run inside the sandbox.

Share it with your team

This is where the file pays for itself. Commit it with the rest of your project:

Host machine

git add sbxenv.yaml
git commit -m "Add a shared sandbox environment"
[main 4f2a9c1] Add a shared sandbox environment
 1 file changed, 16 insertions(+)
 create mode 100644 sbxenv.yaml

Your teammate doesn’t need to know what a kit is, which port the dev server uses, or which flags you settled on. After they pull, it’s one command, run from their copy of the project:

Your teammate's machine

cd ~/code/my-app
sbx env run
── ENVIRONMENT PLAN
   claude-my-app

   kits:
+    - sbx/claude:2.1.267
+    - sbx/playwright:1.61.1

   env:
+    NODE_ENV: development

   ports:
+    - sandbox: 3000
+      host: 3000

   Plan: + 4 to add, ~ 0 to change, - 0 to destroy.

   Approve this plan? [y/N] y

Creating sandbox claude-my-app...
Publishing 127.0.0.1:3000 -> 3000/tcp4
Connected to Claude in sandbox claude-my-app.

They cloned it somewhere else, and it doesn’t matter: the workspace is read from the file’s location, and the name comes from the file, so they get claude-my-app too. Because the kits are pinned, they get the same Claude Code and the same Playwright you have, not whatever was newest when they happened to run it. They see the same plan you saw, and approve it on their own machine, with their own credentials.

That approval is worth a moment. Running a file someone else wrote is a little like running their script, so the plan is your chance to read what it asks for before it happens. And once the sandbox is running, the agent can’t quietly rewrite the file to give itself more next time. When the file sits inside the workspace, as it does here, sbx shares that one file read-only. The agent can edit your code, but not the rules it runs under, just as it can’t change the network policy that governs it.

Update once, and everyone benefits

Say your Playwright tests start running out of memory. Instead of telling everyone to add a flag, you change the file once:

# Add to ~/my-app/sbxenv.yaml
sandboxOptions:
  memory: 8g

Run sbx env plan again, and this time it shows only what moved:

Host machine

sbx env plan
── ENVIRONMENT PLAN
   claude-my-app

   sandboxOptions:
+    memory: 8g    (applies when the sandbox is next created)

   Plan: + 1 to add, ~ 0 to change, - 0 to destroy.

Bumping a pinned kit works the same way. Change :1.61.1 to a newer tag, and the plan shows that one line with a ~, the old version against the new one. The upgrade becomes a reviewed, one-line change in your repository instead of something that happens to each person on a different day.

Some changes apply to a sandbox that already exists. New env values, for example, reach the agent the next time you run sbx env run. Others, like kits, ports, workspaces, and sandboxOptions, are baked in when the sandbox is built, so you remove the sandbox and run the file again to pick them up:

Host machine

sbx env rm
── ENVIRONMENT PLAN
   claude-my-app

-  sandbox: claude-my-app

   Plan: + 0 to add, ~ 0 to change, - 1 to destroy.

   Approve this plan? [y/N] y

Deleting sandbox claude-my-app...
Sandbox 'claude-my-app' removed
sbx env run
── ENVIRONMENT PLAN
   claude-my-app

   kits:
+    - sbx/claude:2.1.267
+    - sbx/playwright:1.61.1

   env:
+    NODE_ENV: development

   sandboxOptions:
+    memory: 8g

   ports:
+    - sandbox: 3000
+      host: 3000

   Plan: + 5 to add, ~ 0 to change, - 0 to destroy.

   Approve this plan? [y/N] y

Creating sandbox claude-my-app...
Publishing 127.0.0.1:3000 -> 3000/tcp4
Connected to Claude in sandbox claude-my-app.

Removing the sandbox deletes the microVM and anything in it that wasn’t in your workspace. Your project files stay where they are.

Commit the change, and your teammates pick it up the next time they pull. Their plan shows them the same new line yours did, so nobody is surprised, and nobody had to follow a message about which flag to add.

Keep your own tweaks out of the shared file

Sometimes you want something just for you, like more memory on a bigger machine. Don’t edit the shared file for that. Put it in a second file that git ignores:

# ~/my-app/local.sbxenv.yaml (add this name to .gitignore)
sandboxOptions:
  memory: 12g

Then pass both files, shared first:

$ sbx env run sbxenv.yaml local.sbxenv.yaml

Later files win: you get 12 GB of memory and everything else from the shared file. Use the same list of files with every sbx env command for that sandbox, including sbx env rm, so they all find the same environment.

Lists are the exception: they add up rather than replace. Don’t repeat a kit from the shared file in your local one, or it ends up in the list twice.

For defaults you want in every project, you can also keep a personal ~/.sbxenv.yaml in your home directory. The environment files documentation covers how it combines with a project’s file.

What to watch out for

An environment file is convenient, and it’s worth knowing where its edges are:

  • Never put a secret value in the file. Anyone who can read the repository can read the file. Each person stores their own credentials on their own machine. If your team needs a shared way to fetch them, the file can point at a password manager or a command instead of a value. See secrets in the documentation.
  • Read the plan, especially on a file you didn’t write. A file can declare commands that run on your machine, outside the sandbox, with your permissions. They always appear in the plan, and sbx asks about them every time. If you don’t recognize one, say no.
  • A local kit starts with ./. A reference like sbx/playwright always means a published kit on Docker Hub, the same way docker pull treats an image name. To use a kit folder that sits next to the file, write the path out: ./kits/my-kit.
  • Some changes mean rebuilding. Kits, ports, workspaces, and sandbox options only take effect on a new sandbox. Anything you installed by hand inside the old one goes with it, so put what you need in the file instead.
  • It’s still experimental. Expect the format to grow and change.

Next steps

You’ve gone from a sandbox you assembled by hand to one anyone can rebuild from a file, and updated it for everyone with one reviewed change.

From here:

  • If you’d rather drive the agent from a desktop app you already use, see Integrating sandboxes into native apps.
  • For every field an environment file supports, including additional workspaces, MCP servers, and arguments that let one file serve several setups, see the environment file reference.
  • When the published kits aren’t enough, you can build your own. See Kits in the Docker Sandboxes documentation.
Learning paths

Part of a learning path

This works on its own and is also included in the paths below. Open a path to see the complete route.