Share feedback
Answers are generated based on the documentation.

sbx env

DescriptionManage sandboxes declaratively from an sbxenv.yaml file
Usagesbx env COMMAND

Experimental

This command is experimental.

Experimental features are intended for testing and feedback as their functionality or design may change between releases without warning or can be removed entirely in a future release.

Description

Manage a sandbox environment declared in an sbxenv.yaml file.

The file describes the agent, optional mixin kits, workspace mounts, environment variables, secrets to provision, and per-service credential bindings. Secrets are provisioned at the environment's sandbox scope so sbx env rm can remove everything it created.

A file may declare its own inputs in an args: block, each with a default or required: true and an optional description, enum, or pattern. Reference one as ${{ env.args.NAME }} anywhere a value appears and supply it with --env-arg NAME=VALUE.

A kits: entry is either a bare reference or a mapping carrying the arguments that kit declares, which --kit-arg overrides per invocation:

kits: - ./mixins/base - source: ./mixins/tool args: version: ${{ env.args.channel }}

A kit source written as an explicit relative path — ./…, ../…, ., .., or one ending in .zip — is resolved against the directory of the file that declares it, so a checked-in file reaches the same kits from wherever sbx is run. Write a local kit that way: a bare kits/tool is as much a registry reference as a directory, so it is left as written and resolves from the current directory.

A workspace: names the directory mounted read/write into the sandbox, resolved against the project directory: the one holding the first PATH, or the current directory when none is named. workspace: . mounts the project from whichever file declares it. Declaring none mounts nothing — as omitting PATH does for sbx create — and the agent works in the container's own filesystem instead of on your files. Unless the file sets name:, the sandbox is named after the mounted directory, or after the project directory when nothing is mounted, so an environment that mounts nothing is still the same sandbox every time.

A lifecycle: block declares commands that run on the host — outside the sandbox, with your own privileges — around the sandbox's life:

lifecycle: initialize: - command: test -d app || git clone https://github.com/acme/app postCreate: - command: ./scripts/seed-fixtures.sh preRemove: - command: ./scripts/archive-state.sh

Each runs through your shell from the project directory — the one holding the first PATH, which is also what a relative "workspace:" resolves against, and is shared by commands merged in from a file elsewhere. Change it per command with workdir:, and cap a command's runtime with timeout:.

"initialize" runs on every "create" and every "run", including one that only attaches, so it can produce the workspace the sandbox mounts; write it to be repeatable. "postCreate" runs once the sandbox exists, and "preRemove" after "sbx env rm" is confirmed but before it deletes anything. Whatever stops preRemove is only a warning, so a teardown that cannot run still cannot make an environment unremovable; what one adds to the environment instead — a stored credential, an approved domain — stops the removal, since what follows would delete it without a plan row ever naming it. "sbx env exec" runs no commands at all.

Commands appear in the environment plan with the directory each runs in, and are approved with it before the invocation does any work. An environment that declares any of them asks on every invocation, whether or not this one is what runs them, since approving a command also trusts whatever it invokes, including a script whose contents change after the answer. Use --skip-host-commands to run none of them.

Everything an environment sets up — host commands, credentials, bindings, MCP registrations, directories, published ports, the sandbox itself and the variables it runs with — is shown as a plan and approved before anything runs:

── ENVIRONMENT PLAN claude-proj

 secrets:
  • anthropic:

  •  ref: op://vault/anthropic/key
    
  •  refresh: 55m
    

    lifecycle: initialize: ~ - command: make setup -> make setup && make seed workdir: /Users/me/proj

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

    Approve this plan? [y/N]

The plan is your file: the same keys, nested the same way, in the order the blocks are declared in, so a line is looked up where it was written. What the plan adds is the margin, and the two values a line moves between. The totals name every symbol the margin can carry: "+ to add" and "~ to change" above, "- to destroy" for what "sbx env rm" takes away, "> to run" for a command that runs again — a command converges to nothing, so it runs on every apply that reaches it — and "! to forget" for a resource this environment applied and no longer declares. Where the file has nothing to say, a note in the margin does: that a resource is missing, or that the work waits for the next create, since a port, a credential, a kit or a postCreate command comes with the sandbox, so attaching to one that already exists leaves it for the next one that is built. A resource that is as it was, and already approved, is left out: what is on screen is what there is to read.

An attribute shows what the environment declares, so an edited kit argument or variable reads as what it was against what it becomes, and a "command:" or "ref:" secret shows where the credential comes from — a command that resolves one runs on this machine. A secret's literal "value:" is the one exception: a plan is both shown here and written to state, so it is named and stands in as a "sha256:" digest.

 kits:

~ - source: ./mixins/tool ~ args: ~ version: 1.2.3 -> 1.2.4 env: ~ GOFLAGS: -mod=mod -> -mod=readonly

What an attribute was is what this environment last applied here, or — for one it approved and never applied, such as a binding or a port answered for while attaching to a sandbox that already exists — what was approved. Either way an edit shows the value the question is about, whatever the row itself does.

An environment file that a mount would hand over read-write — which is what mounting the project directory holding it does — is bound read-only at its own path inside that mount, leaving the rest of it writable. The file decides what a later invocation runs on this machine, so an agent able to edit it decides what the next plan asks about. Declare "sandboxOptions.writableEnvFiles: true" where an agent is meant to edit it; the plan then says the file is writable, as it says when a file sits below a mount's own directory, where renaming that directory reaches it again.

What was approved is recorded per environment under sbx's state directory, not next to the file, so a later invocation asks only about what moved — and applies silently when nothing did. An environment that declares commands running on this machine is asked about on every invocation, changed or not: the answer is about the invocation, and what a command does depends on what the project holds when it runs rather than on the text approved before. "sbx env plan" prints the plan and changes nothing.

Use --auto-approve (-y) where there is no terminal to answer on. Where an environment's commands are your own and run many times a day, "sbx settings set env.rememberHostCommands true" asks about them only when they change.

Commands

CommandDescription
sbx env createexperimental Create a sandbox environment from sbxenv.yaml
sbx env execexperimental Execute a command inside a sandbox environment
sbx env planexperimental Show what an environment would change outside the sandbox
sbx env rmexperimental Remove a sandbox environment and its scoped resources
sbx env runexperimental Create (if needed) and attach to a sandbox environment

Global options

OptionDefaultDescription
--cloudDispatch to Docker Cloud Sandboxes API instead of local sandboxd (supported by a growing set of verbs — run 'sbx --cloud --help' for the current list)
--cloud-api-urlhttps://api.sandboxes-cloud.docker.comCloud Sandboxes API base URL; only used with --cloud. Defaults to prod (https://api.sandboxes-cloud.docker.com). Set DOCKER_CLOUD_API_URL or pass this flag to override; a legacy value ending in /v1 is accepted.
-D, --debugEnable debug logging