8.0 KiB
Configuration Reference
Sandcage uses a layered configuration system. Each layer overrides the previous one:
- Compiled defaults — all fields unset
- Global config (
~/.sandcage/config.toml) — user-wide preferences (TOML format) - Project config (
.sandcage.yml) — per-project settings, checked into version control (YAML format) - Local config (
.sandcage.local.yml) — personal overrides, gitignored (YAML format)
Later layers win. Fields not set in a layer are inherited from the layer below.
Fields
env
Environment variables passed into the container at runtime.
- Type: Map of string keys to string values
- Valid in: Global, Project, Local
- Behavior: Passed as
-e KEY=VALUEflags todocker compose run
# .sandcage.yml
env:
RUST_LOG: debug
DATABASE_URL: "postgres://localhost:5432/dev"
# ~/.sandcage/config.toml
[env]
EDITOR = "vim"
packages
System packages to install in the container image (apt packages).
- Type: List of strings
- Valid in: Global, Project, Local
packages:
- ripgrep
- fd-find
- postgresql-client
toolchains
Language toolchains to install, keyed by language name and version.
- Type: Map of string keys to string values
- Valid in: Global, Project, Local
toolchains:
rust: "1.78"
node: "20"
python: "3.12"
mounts
Additional volume mounts passed to the container. Each entry must contain a : separator.
- Type: List of strings in Docker mount format (
<source>:<target>[:<options>]) - Valid in: Global, Project, Local
- Behavior: Tilde (
~) in the source path is expanded to the host home directory. Entries without:are skipped with a warning.
mounts:
- /data/models:/models:ro
- ~/.gitconfig:/home/agent/.gitconfig:ro
- /tmp/cache:/cache
shell
Default shell inside the container.
- Type: String
- Valid in: Global, Project, Local
shell: zsh
justfile
Path to a justfile to make available in the container.
- Type: File path (string)
- Valid in: Global, Project, Local
justfile: ./project.justfile
dockerfiles
Custom Dockerfile overrides for image builds. Keyed by image short name. Values can be a path to a Dockerfile or a directory containing one.
- Type: Map of string keys to file paths
- Valid in: Global, Project (project overrides require the project to be in
trusted_projects) - Behavior: Project-level Dockerfile overrides are ignored unless the project directory is listed in
trusted_projectsin the global config.
# .sandcage.yml (only applied if project is trusted)
dockerfiles:
sandcage: ./docker/Dockerfile.custom
# ~/.sandcage/config.toml
[dockerfiles]
sandcage = "/home/user/my-dockerfiles/sandcage/"
trusted_projects
List of project directories allowed to use Dockerfile overrides.
- Type: List of file paths
- Valid in: Global only (a project cannot self-trust)
- Behavior: Read exclusively from
~/.sandcage/config.toml. Projects withdockerfilesset are ignored unless their path (or a parent) appears here.
# ~/.sandcage/config.toml
trusted_projects = [
"/home/user/projects/my-trusted-project",
"/home/user/work",
]
container_workspace
Override the workspace path inside the container. Must be an absolute path (starting with /). Relative paths are ignored with a warning.
- Type: String (absolute path)
- Valid in: Global, Project, Local
- Default:
/workspace/<project-directory-name>(derived from the host workspace folder name)
container_workspace: /workspace/my-app
agent_args
Default arguments passed to agents on every invocation. Keyed by service name. These are appended after the service name but before any extra CLI arguments.
- Type: Map of service name to list of strings
- Valid in: Global, Project, Local
- Behavior: Only the args for the invoked service are used. When running with
--shell, agent_args are skipped entirely.
agent_args:
claude:
- "--dangerously-skip-permissions"
codex:
- "--full-auto"
# ~/.sandcage/config.toml
[agent_args]
claude = ["--dangerously-skip-permissions"]
ssh_mode
Controls how SSH keys are made available inside the container.
- Type: String — one of
"volume","bind", or"none" - Valid in: Global, Project, Local
- Default: No SSH mount (equivalent to
"none")
| Value | Behavior |
|---|---|
volume |
Mounts a named Docker volume (sandcage-ssh) at /home/agent/.ssh (read-only). Populate it with sandcage setup ssh. |
bind |
Bind-mounts ~/.ssh from the host at /home/agent/.ssh (read-only). |
none |
No SSH directory is mounted. |
ssh_mode: volume
When using volume mode, run sandcage setup ssh --refresh after changing keys on the host.
ssh_keys
Declares which SSH keys to copy into the volume when using sandcage setup ssh. Each entry specifies a host and identity file.
- Type: List of objects with
host(string) andidentity_file(string) fields - Valid in: Global, Project, Local
ssh_keys:
- host: github.com
identity_file: ~/.ssh/id_ed25519
- host: gitea.internal
identity_file: ~/.ssh/work_gitea
services
Override the enabled state of built-in services. Keyed by service name.
- Type: Map of service name to object with optional
enabled(boolean) field - Valid in: Global, Project, Local
- Behavior: Disabled services cannot be started with
sandcage <service>and are excluded from generated compose files.
services:
gemini:
enabled: false
codex:
enabled: false
# ~/.sandcage/config.toml
[services.codex]
enabled = false
default_services
Controls which services sandcage build prepares by default (when no service names are given on the CLI).
- Type: List of strings (service names)
- Valid in: Global, Project, Local
default_services:
- claude
- shell
image
The Docker image name used for building and running containers.
- Type: String
- Valid in: Global, Project, Local
- Default:
"sandcage"
# .sandcage.yml
image: my-custom-sandcage
# ~/.sandcage/config.toml
image = "my-custom-sandcage"
Services
Sandcage ships with four built-in services, all enabled by default:
| Service | Description | Config directory |
|---|---|---|
claude |
Claude Code agent | .claude |
codex |
Codex agent | .codex |
gemini |
Gemini CLI agent | .gemini |
shell |
Interactive zsh shell | (none) |
Agent services are installed on first run into the persistent home directory (~/.sandcage/). They auto-update on subsequent runs.
To disable a service you do not use:
services:
codex:
enabled: false
gemini:
enabled: false
Disabled services are excluded from Docker compose generation and cannot be invoked.
Examples
Minimal project config
# .sandcage.yml
toolchains:
node: "20"
packages:
- ripgrep
Full-featured project config
# .sandcage.yml
env:
RUST_LOG: debug
NODE_ENV: development
packages:
- ripgrep
- fd-find
- postgresql-client
toolchains:
rust: stable
node: "20"
mounts:
- /data/models:/models:ro
shell: zsh
agent_args:
claude:
- "--dangerously-skip-permissions"
services:
gemini:
enabled: false
Local overrides (gitignored)
# .sandcage.local.yml
ssh_mode: volume
ssh_keys:
- host: github.com
identity_file: ~/.ssh/id_ed25519
env:
SECRET_API_KEY: "sk-local-dev-key"
mounts:
- ~/.aws:/home/agent/.aws:ro
Global config
# ~/.sandcage/config.toml
shell = "zsh"
[env]
EDITOR = "vim"
[agent_args]
claude = ["--dangerously-skip-permissions"]
[services.codex]
enabled = false
trusted_projects = [
"/home/user/work/trusted-project",
]