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
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=developmentThen 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
sbxCLI, installed and signed in. Follow Install Docker Sandboxes, then runsbx 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: 3000Reading it top to bottom:
schemaVersionis the file format version. It’s always"1"for now.nameis the sandbox’s name, the same one you passed with--name.agentnames the agent the sandbox launches. For a kit likesbx/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.kitslists every kit that goes into the sandbox. That includes the workload,sbx/claude, which you passed as the first argument tosbx run, and the Playwright mixin you added with--kit.envsets environment variables inside the sandbox.portspublishes 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: 8gRun 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: 12gThen pass both files, shared first:
$ sbx env run sbxenv.yaml local.sbxenv.yamlLater 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
secretsin 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
sbxasks about them every time. If you don’t recognize one, say no. - A local kit starts with
./. A reference likesbx/playwrightalways means a published kit on Docker Hub, the same waydocker pulltreats 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.
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.
Learning path
Get Started with Docker Sandboxes
Step 4 of 5
- Step 1: What is a sandbox?
- Step 2: Run your first AI agent in a Docker Sandbox
- Step 3: Customize your sandbox with kits
- Step 4: Define and share your sandbox environment (this page)
- Step 5: Integrating sandboxes into native apps
Table of contents