Docker Sandboxes: Declarative sbxenv.yaml Environments
SkillFiles & storageWrite and manage sbxenv.yaml sandbox configs through the docker claude skill for reproducible container setups.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Docker Sandboxes: Declarative sbxenv.yaml Environments skill
About this skill
Use this skill when authoring, planning, or running a declarative `sbxenv.yaml` file for Docker Sandboxes (`sbx env create/run/plan/exec/rm`), even if the user just says they want to "check in a sandbox config", "make onboarding reproducible for a sandbox", "run a setup script before the agent start
What this skill tells your AI
The instructions your AI receives, as published by docker/skills in skills/docker-sandboxes-env/SKILL.md and read by ahel’s review.
Overview
sbxenv.yaml (schemaVersion "1", EXPERIMENTAL) declaratively describes one
sandbox environment — agent, mixin kits, workspace mounts, environment
variables, secrets/registries/bindings to provision, MCP servers, ports, and
host-side lifecycle commands — so sbx env create|run|plan|exec|rm can stand
it up and tear it down reproducibly instead of a long flag invocation. This
skill owns that file format end to end. It delegates the sandbox lifecycle
semantics it wraps, the credential/network model it provisions into, and the
kit schema its kits: entries reference, to their own skills.
When to use this skill
Activate this skill when:
- The user wants a checked-in, reproducible definition of a sandbox
environment instead of a long
sbx create/sbx runcommand line. - The user wants host-side setup/teardown commands (cloning a repo, seeding fixtures, archiving state) tied to a sandbox's create/attach/remove lifecycle.
- The user wants to parameterize a shared environment file with named
arguments (
args:+--env-arg). - The user is debugging why
sbx env create/runis asking for approval, or why a file, kit, or secret it declares was skipped or flagged.
Do not use this skill when
Do not use this skill when:
- The task is the underlying
sbx create/run/rmflag-based workflow with nosbxenv.yamlfile involved — usedocker-sandboxes-lifecycle. - The task is choosing network policy or storing a secret/registry
credential independent of any environment file — use
docker-sandboxes-network-credentials(this skill'ssecrets:/registries:/bindings:blocks provision into that same store, but do not redefine its rules here). - The task is authoring the kit
spec.yamlakits:entry points at — usedocker-sandboxes-kits.
Core guidance
File resolution and required fields
- The file
sbx envreads from a directory is exactlysbxenv.yaml— no other name, and a directory-named.sbxenv.yamlat the project level is not read as a project's own file (only the home-directory base layer uses that hidden name; see below). - Every environment file requires
schemaVersion: "1"andagent:(a built-in agent name or the manifest name of an agent kit supplied viakits:). Everything else is optional.agent: shellneeds no credentials and is the simplest way to validate a file's mechanics. sbx env create|run|plan|exec|rmaccept one or morePATHarguments. EachPATHis either a directory (resolved to<PATH>/sbxenv.yaml) or the file itself. Passing more than one deep-merges them in declaration order —docker compose -f-style semantics: later files override earlier ones, mappings merge key-by-key, sequences concatenate.sbx env create sbxenv.yaml override.yaml
Naming, workspace, and the .sbxenv.yaml user base layer
- Unless the file sets
name:or--nameoverrides it, the sandbox is named after the mounted directory (or the project directory when nothing is mounted) — so an environment that mounts nothing is still the same sandbox every time it is applied. Two different environment files in the same directory derive the same sandbox name and collide unless each sets its ownname:(or you pass a distinct--nameper invocation) — always give each environment its own explicitname:when more than one may exist in the same directory. workspace:names the read/write mount, exactly likesbx create's omitted-path behavior: omittingworkspace:mounts nothing at all. A relativeworkspace:path resolves against the directory of the file that declares it —workspace: .mounts the directory the file sits in.${{ env.projectDir }}names the project directory (the one holding the firstPATH, or cwd when none is named);${{ env.fileDir }}names the declaring file's own directory. Nothing else is expanded — a bare$is literal text, so a value written for the container (PATH: $PATH:/opt/bin) reaches it unchanged.workspace: . # mounts the directory this file sits in # workspace: ${{ env.projectDir }} # mounts the project directory explicitly- Relative kit sources follow the same file-directory anchoring rule as
workspace:(seedocker-sandboxes-kitsfor kit reference syntax). - Files within a mounted workspace get default read-only masking, and that
protection is complete only when the file sits directly at the mount's own
root. A read-only bind at the mount point cannot be renamed by the
sandbox — there is nothing above it inside the mount to rename. But an
environment file in a subdirectory of a read-write mount is protected
only at its current path: the sandbox can rename the containing directory
(which it can write to) and then recreate the original path itself,
landing a sandbox-controlled file back where the read-only bind no longer
applies.
sbx env plancalls this gap out explicitly for a file that is not at a mount's root. Do not claim renaming the containing directory creates no gap — for anything but the mount root, it does. - With no
PATHgiven, an.sbxenv.yamlin the home directory is merged underneath as a base layer for defaults shared across projects; naming anyPATHskips this layer entirely. The base layer may not setname:(which identifies one project) and itsworkspace:must be rooted at${{ env.projectDir }}— any other value would mount one fixed directory under every project that merges it.
args: — parameterizing a shared file
- Declare named inputs under
args:, each with adefault(making it optional,default: ""counts as a real default) orrequired: true(mutually exclusive), plus optionaldescription,enum, orpattern. - Reference one as
${{ env.args.NAME }}anywhere a value appears in the file, and supply it with--env-arg NAME=VALUE(repeatable) or--env-args-file PATH.
lifecycle: — host commands and the approval plan
lifecycle:declares shell commands that run on the host, outside the sandbox, with your own privileges — not inside the container. Three phases, run in this order per invocation:initialize— runs on everycreateandrun, including one that only attaches to an existing sandbox. It is the one phase that can produce what the environment needs to exist (a cloned workspace, a generated file), so it must be idempotent — it reruns on every reattach.postCreate— runs once, after the sandbox exists, before an interactive attach takes the terminal.preRemove— runs beforesbx env rmdeletes the sandbox, whilesbx env execcan still reach it. A failingpreRemoveis only a warning — the failure itself does not block removal. After the hook, removal rechecks the approved destroy plan and sandbox identity. A new credential or changed binding not covered by that approval, or a replacement sandbox under the same name, stops removal before deletion. Review the new destroy plan before retrying.sbx env execruns no lifecycle commands at all, and requires the sandbox to already exist — it does not create one. Runsbx env create/sbx env runfirst.
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- Every command runs through the shell from the project directory by
default (override per-command with
workdir:; bound its runtime withtimeout:). - A file that declares any lifecycle command is asked about on every
invocation that reaches it, whether or not this particular invocation
changed anything — approving a command also trusts whatever it invokes,
including a script whose contents can change after the answer, so the
question is repeated rather than remembered by default. The one exception:
sbx settings set env.rememberHostCommands truemakes it ask again only when the commands actually change. Never treat an untrusted file's or an untrusted kit's lifecycle commands as pre-approved, and never enablerememberHostCommandsfor a file whose commands you have not reviewed. An environment that declares no host commands at all, and whose config is otherwise unchanged from what was last approved, applies silently with no prompt. Use--skip-host-commandsto run none of the declared commands for one invocation.
The environment plan: what it is and is not
sbx env plan [PATH...]prints everything applying the file would set up — host commands, credentials/bindings, MCP registrations, directories, published ports, the sandbox itself, and its variables — compared against what was last applied/approved. It changes nothing.sbx env create/sbx env runshow the same plan and require approval before doing any work (--auto-approve/-yskips the prompt for non-interactive use — never default to-yfor a file or kit you have not reviewed). A secret's literalvalue:is the one field shown both in the plan and recorded to state as asha256:digest rather than in the clear; aref:/command:secret shows where the credential comes from, not its resolved value.
Secrets, registries, and bindings scoped to the environment
secrets:andregistries:provision into the same credential storesbx secret setuses, at this environment's sandbox scope, sosbx env rmcan remove exactly what it created. Each entry uses the samevalue/ref/commandshape assbx secret set(exactly one of the three) — seedocker-sandboxes-network-credentialsfor what those mean at runtime and why a literal secret value should not otherwise appear in a checked-in file.bindings:are per-service credential bindings merged into the user's globalcredentials.yaml; unlikesecrets:/registries:, they are left in place by default bysbx env rm(they are user-wide and may be shared with other sandboxes/environments) — pass--prune-bindingsto also remove them.- Never write a literal secret value directly into a checked-in
sbxenv.yaml. Useref:(1Password/AWS Secrets Manager) orcommand:so the value never lives in the file at all; if a literalvalue:is used transiently, both the plan and state show only its digest, but the original environment file still contains the plaintext secret. See the labeledsecrets:fragment below for the shape — it is intentionally not part of the minimal asset, which needs no credentials at all to validate.# OPTIONAL fragment — add only if this environment actually needs a # credential; the minimal asset omits this entirely. secrets: anthropic: ref: op://Private/Anthropic/api-key # never a literal `value:` in a checked-in file refresh: 55m
kits:, additionalWorkspaces:, mcp:, ports:, and sandboxOptions:
kits:composes mixin kits (and, exactly once, an agent kit whose name matchesagent:) onto the base agent; a relative source anchors to the declaring file's own directory, the same rule asworkspace:.additionalWorkspaces:mounts extra directories beyond the primaryworkspace:(a file cannot declare one without the other) — thesbxenv.yamlequivalent ofsbx run's extra positional workspace arguments with:ro.mcp.servers:registers MCP servers on the host and adds them to the sandbox's fixed (static) MCP set at create time; registrations are host-global and left in place bysbx env rm.ports:pins explicit host-port bindings for container ports the sandbox exposes — the equivalent ofsbx ports --publish— and is torn down automatically whensbx env rmdeletes the sandbox.sandboxOptions:(beyondwritableEnvFiles, below) maps onto the remainingsbx createflags:template,memory,cpus,pullPolicy,profile,skills.
See references/env-schema-fields.md for the exact field shapes, required
keys, and a YAML example for each of the five blocks above.
sandboxOptions.writableEnvFiles — a deliberate, explicit downgrade
- By default, every environment file mounted inside the workspace is read-only at its own path, even though the rest of the mount is writable. This stops an agent editing the very file that decides what host lifecycle commands and secret-resolving commands run on your machine on the next invocation.
- Set
sandboxOptions.writableEnvFiles: trueonly where an agent is deliberately meant to edit its own environment file. This is a real security downgrade — the plan then reports the file as writable — so treat it the same as any other explicit trust decision, not a default. - The protection is complete only at a mount's own root. A file placed
directly at the root of a read-write mount cannot be reached even by
renaming, because the sandbox cannot rename the mount point itself. A file
in a subdirectory of that mount is a different case: it is read-only at
its current path, but the sandbox can rename the directory holding it
(which it can write to) and recreate a file at the original path, ending
up with a sandbox-controlled file there.
sbx env planflags this gap for a file that is not directly at a mount's root — read the plan's output rather than assuming renaming is always harmless.
Related skills
- For the
sbx create/run/rmflag-based workflow this file wraps, usedocker-sandboxes-lifecycle. - For what
secrets:/registries:/bindings:mean at runtime, and for configuring network policy independent of any environment file, usedocker-sandboxes-network-credentials. - For the schema of the kit
spec.yamlakits:entry (oragent:pointing at an agent kit) references, usedocker-sandboxes-kits.
References
references/sources.md— provenance for every rule above (help captures, source paths, docs URLs).references/env-schema-fields.md— exact field shapes and YAML examples forkits:,additionalWorkspaces:,mcp:,ports:, andsandboxOptions:.
Assets
assets/sbxenv.yaml— a complete, minimal, safe example: ashellagent mounting the declaring file's own directory, one static env var, and no credentials at all — it validates and plans without any onboarding authentication.
Checks
checks/verification.md— Verification runbook for sbxenv.yaml commands (unexecuted runbook; run manually with an isolated, uniquely-named--app-name, never with real secret values or untrusted lifecycle commands auto-approved).
Signals
- GitHub stars
- 410
- Forks
- 21
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
docker-sandboxes-env- Source
- github.com/docker/skills