Reusable Docker Sandboxes (sbx) bases for shell, Claude Code and OpenCode, plus sbx-up, which
recreates a project's sandbox from them with the project's own additions. Renovate keeps the
base image, tool versions and the agent mixins up to date.
shell-base/: v3 workload kit. Ubuntudocker/sandbox-templates:shell-dockerwith Docker, mise (node, hunk, helix), supply-chain presets for npm/pnpm/uv, the shared agent guidance (config/AGENTS.md), a default helix config (config/helix/) and a base network allow list. sbx builds it from this directory. Project-specific tools such as pnpm or uv go in the project's.sbx-kits/.claude-mixin.ref: Docker'sclaude-mixinkit, pinned by digest. It adds Claude Code, API key / subscription sign-in through the sbx proxy (real credentials stay on the host), session volumes and Claude's allow list on top of shell-base.opencode-mixin.ref: Docker'sopencode-mixinkit, pinned by digest tolatest(its version tags need a newer sbx, seerenovate.json). It adds OpenCode, optional provider credentials through the sbx proxy and OpenCode's allow list on top of shell-base.bin/sbx-up: composes shell-base and a mixin with a project's mixins and (re)creates the sandbox.project-template/: a starting.sbx-kits/for projects.
On the host (macOS), where sbx runs; sbx-up doesn't work inside a sandbox.
-
Check that
sbxworks:sbx versionandsbx ls. -
Clone this repo.
sbx-upfindsshell-base/and the*-mixin.reffiles through its own location, so the clone can live anywhere:git clone git@github.com:simbados/sandbox-templates.git ~/projects/sandbox-templates -
Put
sbx-uponPATH, either as a symlink into a directory already on it:mkdir -p ~/.local/bin ln -s ~/projects/sandbox-templates/bin/sbx-up ~/.local/bin/sbx-up # only if ~/.local/bin isn't on PATH yet (zsh): echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc
or with the repo's
bin/onPATH:echo 'export PATH="$HOME/projects/sandbox-templates/bin:$PATH"' >> ~/.zshrc. -
Check:
sbx-up -hprints the usage, andsbx-up -n shellin any directory prints thesbxcommands it would run without running them. -
For Claude Code, optionally store an API key once:
sbx secret set anthropic. Without one, use/logininside Claude Code; the sbx proxy keeps the real tokens on the host either way. -
In each project, copy the template mixin and commit it with the project:
sbx-up kit # copies project-template/.sbx-kits/ into the current directoryMake sure
.sbx-kitsisn't matched by a global gitignore (git check-ignore -v .sbx-kits).
To update the bases later, git -C ~/projects/sandbox-templates pull, then sbx-up -r in a
project rebuilds its sandbox from them (sbx reuses its build cache when nothing changed).
From a project directory, on the host:
sbx-up shell # shell-base + the project's .sbx-kits/ mixins, opens fish
sbx-up claude # ... + claude-mixin, starts Claude Code in auto mode
sbx-up opencode # ... + opencode-mixin, starts OpenCode
sbx-up -r claude # rebuild: remove the sandbox and create it from the current kits
sbx-up -n claude # print the sbx commands instead of running them
sbx-up shell ~/projects/x -- -lc 'make test' # other directory, own launch arguments
sbx-up sealed claude # throwaway sandbox, see below
sbx-up sealed -c test claude # another one next to it, named sealed-claude-test
sbx-up sealed -k claude # sealed, but kept after the session; run again to reattach
sbx-up kit [PATH] # copy project-template/.sbx-kits/ into a projectThe sandbox is named <project>-<flavor>, e.g. <project>-shell or <project>-claude. If it already exists, sbx-up
starts it again (sbx run --name, which also restarts a stopped one) and attaches to it; it only
creates a sandbox when none exists. A sandbox keeps the kits it was created with, so after
git pull here or an edit in .sbx-kits/, run sbx-up -r: that removes the sandbox and creates
it from the current kits. Anything installed by hand in the old sandbox is gone then, and so is
everything else sbx rm removes with it ("all associated resources"); put lasting setup in
.sbx-kits/.
sbx-up sealed shell|claude|opencode starts a throwaway sandbox named sealed-<flavor>.
It mounts an empty directory (~/.cache/sbx-up/sealed-<flavor>) instead of a project and adds no
project mixins. It runs sbx run --rm (sbx 0.47.0 or later), so sbx removes the sandbox when the
session ends. A sandbox left behind by an interrupted run is removed, and the directory emptied, at
the next sealed start. Only one of each name can run at a time, since a new one replaces the old;
-c NAME gives a sealed sandbox its own name and directory (sbx-up sealed -c test claude runs
sealed-claude-test on ~/.cache/sbx-up/sealed-claude-test), so several can run side by side.
-k keeps the sandbox and its directory when the session ends (no --rm); the next
sbx-up sealed -k with the same flavor and -c name reattaches to it, and sbx-up -r sealed -k
starts it fresh. A sealed start without -k still replaces it, so give kept ones a -c name if you
also use plain sealed sandboxes of that flavor.
Before each start, sbx-up also copies config/skills/ into the folder sbx mounts read-only at
~/.claude/skills in every sandbox (~/Library/Application Support/com.docker.sandboxes/sandboxes/agent-skills
on the Mac, not the Mac's own ~/.claude/skills). Only changed skills are copied; bin/install-skills
does the same by hand.
sbx-up kit copies the template into a project's .sbx-kits/ and leaves mixins the project
already has alone.
Claude Code signs in with /login inside the sandbox (the proxy keeps the real tokens on the
host) or with an API key stored once on the host: sbx secret set anthropic.
Copy project-template/.sbx-kits/ into a project with sbx-up kit. Each .sbx-kits/<name>/<name>.yaml is a v3 mixin:
allow-listed hosts, install commands and files, and optionally a <name>.dockerfile for tools
that are better built once. See project-template/README.md.
shell-base/shell-base.dockerfile and shell-base/shared/ (copies of config/ and keys/,
because a kit can only use files inside its own directory) are generated from
shell-base/template.yaml and tools/*.dockerfile. After editing any of those, or config/,
run python3 scripts/generate_dockerfile.py.
Background and open points: docs/kits-v3-migration.md.