Configuration
Read when:
- adding a new config key, env override, or flag;
- debugging "why is Crabbox using value X here?";
- onboarding a repo and choosing what belongs in repo config vs user config;
- reviewing the YAML schema that
crabbox config showandcrabbox init
emit.
Crabbox configuration is layered. The CLI loads values from five sources and merges them in a deterministic order. Each source is optional - the binary boots with sane defaults for everything.
#Precedence
flags > env > repo-local crabbox.yaml/.crabbox.yaml > user config > defaults
Reading order is the lowest precedence first: defaults are applied, then overridden by user config, then repo config, then env vars, then flags. Every override only replaces fields that are explicitly set; unset fields fall through.
crabbox config show prints the merged configuration as the CLI sees it after all five layers run. --json is stable enough to diff in scripts. crabbox config path prints the user config file path so other tools can edit it without parsing prose.
#File Locations
macOS user: ~/Library/Application Support/crabbox/config.yaml
Linux user: ~/.config/crabbox/config.yaml
XDG override: $XDG_CONFIG_HOME/crabbox/config.yaml
repo: ./crabbox.yaml or ./.crabbox.yaml at repo root
explicit: $CRABBOX_CONFIG (any path)
If CRABBOX_CONFIG is set, it overrides the repo-local search and replaces the effective repo config. User config is never replaced by the env override.
State that does not belong in either YAML file:
- live lease records (those are coordinator-owned);
- per-lease SSH private keys (those live under the user config dir but not in
- provider secrets (those live in the broker environment, your shell env, or
config.yaml);
a credential manager).
#YAML Schema
The full schema below merges what crabbox init emits and what advanced operators set in user config. Most repos only need a small subset.
#Top-level
broker:
url: https://crabbox.openclaw.ai
provider: aws
token: <signed-github-token-or-shared-token>
access:
clientId: <cloudflare-access-service-token-id>
clientSecret: <cloudflare-access-service-token-secret>
provider: aws # default provider when --provider is not set
target: linux # default target OS
windows:
mode: normal # normal or wsl2 when target=windows
profile: project-check
class: beast # standard | fast | large | beast
type: c7a.48xlarge # explicit provider type, overrides class fallback
network: auto # auto | tailscale | public
lease:
idleTimeout: 30m
ttl: 90m
#Jobs
Named jobs live in repo config and describe reusable Crabbox orchestration, not project logic baked into the binary. Use them for common "warm a box, hydrate it with GitHub Actions, run the repo command, clean up" flows. See Jobs for lifecycle details and the field contract.
jobs:
openclaw-wsl2:
provider: aws
target: windows
windows:
mode: wsl2
class: beast
market: on-demand
idleTimeout: 240m
hydrate:
actions: true
waitTimeout: 45m
keepAliveMinutes: 240
actions:
workflow: hydrate.yml
job: hydrate
shell: true
command: >
corepack enable &&
pnpm install --frozen-lockfile &&
CI=1 NODE_OPTIONS=--max-old-space-size=4096 pnpm test
stop: always
Run with:
crabbox job run openclaw-wsl2
job run --dry-run <name> prints the underlying warmup, actions hydrate, run, and stop commands.
#Capacity
capacity:
market: spot # spot | on-demand
strategy: most-available
fallback: on-demand-after-120s
hints: true
regions:
- eu-west-1
- us-east-1
availabilityZones:
- eu-west-1a
- eu-west-1b
largeClasses:
- large
- beast
#AWS
aws:
region: eu-west-1
ami: ami-0123456789abcdef0
securityGroupId: sg-0abcdef0123456789
subnetId: subnet-0abcdef0123456789
instanceProfile: crabbox-runner
rootGB: 400
sshCidrs:
- 203.0.113.0/24
macHostId: h-0123456789abcdef0
#Hetzner
Hetzner credentials and image come from broker-side config. Repos do not need a hetzner: block unless they pin a class or location.
#Google Cloud
provider: gcp
gcp:
project: example-project
zone: europe-west2-a
network: default
rootGB: 400
#Proxmox
provider: proxmox
proxmox:
apiUrl: https://pve.example.test:8006
tokenId: crabbox@pve!ci
node: pve1
templateId: 9000
storage: local-lvm
bridge: vmbr0
user: crabbox
workRoot: /work/crabbox
Put tokenSecret in a private config file or use CRABBOX_PROXMOX_TOKEN_SECRET; do not pass it as a command-line flag.
#Static SSH
provider: ssh
target: macos
static:
host: mac-studio.local
user: steipete
port: "22"
workRoot: /Users/steipete/crabbox
#Blacksmith Testbox
provider: blacksmith-testbox
blacksmith:
org: openclaw
workflow: .github/workflows/ci-check-testbox.yml
job: test
ref: main
idleTimeout: 90m
debug: false
#Namespace Devbox
provider: namespace-devbox
namespace:
image: builtin:base
size: M
repository: github.com/openclaw/crabbox
site: ""
volumeSizeGB: 100
autoStopIdleTimeout: 30m
workRoot: /workspaces/crabbox
deleteOnRelease: false
#Daytona
provider: daytona
daytona:
snapshot: openclaw-crabbox
apiKey: <daytona-api-key> # prefer DAYTONA_API_KEY env
#E2B
provider: e2b
e2b:
template: base
workdir: crabbox
apiUrl: https://api.e2b.app
domain: e2b.app
Keep E2B_API_KEY or CRABBOX_E2B_API_KEY in the shell or credential manager. Repo config should select templates and workdirs, not hold API keys.
#Modal
provider: modal
modal:
app: crabbox
image: python:3.13-slim
workdir: /workspace/crabbox
python: python3
Authenticate the local Modal Python client with python3 -m modal setup or MODAL_TOKEN_ID / MODAL_TOKEN_SECRET. Repo config should select app/image and workdir only; tokens do not belong in YAML or command-line flags.
#Semaphore
provider: semaphore
semaphore:
host: myorg.semaphoreci.com
project: my-app
machine: f1-standard-2
osImage: ubuntu2204
idleTimeout: 30m
Keep CRABBOX_SEMAPHORE_TOKEN or SEMAPHORE_API_TOKEN in the shell or credential manager. User config may set host/project defaults; repo config should only pin Semaphore when the repo intentionally depends on that CI environment.
#Sprites
provider: sprites
sprites:
apiUrl: https://api.sprites.dev
workRoot: /home/sprite/crabbox
Keep CRABBOX_SPRITES_TOKEN, SPRITES_TOKEN, SPRITE_TOKEN, or SETUP_SPRITE_TOKEN in the shell or credential manager. Repo config should select the work root only when the repo intentionally depends on a Sprites layout. The authenticated sprite CLI must also be on PATH.
#Sync
sync:
delete: true
checksum: false
gitSeed: true
fingerprint: true
baseRef: main
timeout: 15m
warnFiles: 50000
warnBytes: 5368709120
failFiles: 150000
failBytes: 21474836480
allowLarge: false
exclude:
- node_modules
- .turbo
- dist
A .crabboxignore file at the repo root appends to sync.exclude. See Sync for the matcher rules.
#Env Forwarding
env:
allow:
- CI
- NODE_OPTIONS
- PROJECT_*
env.allow is name-based and supports trailing wildcards. Crabbox forwards matching local env vars to the remote command. Secrets do not belong in env.allow; pass them through provider-side mechanisms.
#Run Preflight
run:
preflightTools:
- node
- bun
- docker
run.preflightTools configures which built-in probes crabbox run --preflight executes before the remote command. The CLI flag --preflight-tools node,bun,docker overrides this list for one run. Use default to include Crabbox's default built-ins and none to print only the workspace summary. Preflight probes only report availability; they do not install toolchains or mutate the machine.
#Actions
actions:
workflow: .github/workflows/crabbox.yml
job: test
ref: main
fields:
- crabbox_docker_cache=true
runnerLabels:
- crabbox
ephemeral: true
runnerVersion: latest
#Cache
cache:
pnpm: true
npm: true
docker: true
git: true
maxGB: 80
purgeOnRelease: false
#Results
results:
junit:
- junit.xml
- reports/junit.xml
#SSH
ssh:
key: ~/.ssh/id_ed25519
user: crabbox
port: "2222"
fallbackPorts:
- "22"
#Tailscale
tailscale:
enabled: false
tags:
- tag:crabbox
hostnameTemplate: crabbox-{slug}
authKeyEnv: CRABBOX_TAILSCALE_AUTH_KEY
exitNode: ""
exitNodeAllowLanAccess: false
#Mediated Egress
Mediated egress is a browser/app QA feature where a lease exits to the internet through an operator machine over the Cloudflare Worker mediator. The first implementation is opt-in and profile-based.
egress:
enabled: false
listen: 127.0.0.1:3128
browserProxy: true
profiles:
discord:
allow:
- discord.com
- "*.discord.com"
- discordcdn.com
- "*.discordcdn.com"
slack:
allow:
- slack.com
- "*.slack.com"
- slack-edge.com
- "*.slack-edge.com"
See Mediated egress for the design, security model, and command surface. The current CLI ships built-in discord and slack profiles; the YAML shape is the intended config surface for making those profiles user-configurable.
#Profiles
Profiles are named bundles of config that get applied as a layer on top of user/repo config. They live under a profiles: map and are selected by --profile or profile: in repo config.
profiles:
project-check:
class: beast
sync:
baseRef: main
env:
allow:
- PROJECT_*
smoke:
class: standard
lease:
ttl: 30m
Use profiles when one repo has multiple test lanes with different machine classes, sync rules, or env allowlists. A repo without profiles never needs the block.
#Machine Classes
A machine class is a provider-agnostic name for "standard", "fast", "large", or "beast" capacity. Each provider maps the class to a list of concrete instance/server types and falls back through the list when the first candidate cannot be provisioned.
| Class | Intent |
|---|---|
standard | typical CI lane |
fast | ~2x more cores than standard for parallel-friendly suites |
large | memory-heavy or many-process workloads |
beast | maximum capacity within the provider's burstable family |
Class-to-type mappings live in Providers. When you set type:, that exact provider type wins and the class is ignored. The --type and type: paths intentionally do not fall back; they fail loud if the provider rejects the type.
#Environment Variables
Every YAML key has a CRABBOX_* env override. The full list is in CLI. Common ones:
CRABBOX_COORDINATOR
CRABBOX_COORDINATOR_TOKEN
CRABBOX_PROVIDER
CRABBOX_TARGET
CRABBOX_PROFILE
CRABBOX_DEFAULT_CLASS
CRABBOX_IDLE_TIMEOUT
CRABBOX_TTL
CRABBOX_NETWORK
CRABBOX_OWNER
CRABBOX_ORG
Provider credentials live outside the Crabbox env namespace because they are provider-native:
HCLOUD_TOKEN / HETZNER_TOKEN
AWS_PROFILE / AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN
GOOGLE_APPLICATION_CREDENTIALS / GOOGLE_CLOUD_PROJECT
CRABBOX_PROXMOX_TOKEN_ID / CRABBOX_PROXMOX_TOKEN_SECRET
DAYTONA_API_KEY / DAYTONA_JWT_TOKEN
BLACKSMITH_* (read by the Blacksmith CLI)
ISLO_API_KEY (read by the Islo SDK)
SEMAPHORE_API_TOKEN / CRABBOX_SEMAPHORE_TOKEN
E2B_API_KEY / CRABBOX_E2B_API_KEY
MODAL_TOKEN_ID / MODAL_TOKEN_SECRET
#What Belongs Where
| Setting | User config | Repo config | Profile | Notes |
|---|---|---|---|---|
broker.url and broker.token | yes | no | no | Per-machine identity. |
provider, class, type | optional default | yes | yes | Per-repo defaults; profiles for lanes. |
sync.exclude, sync.fingerprint, sync.baseRef | no | yes | yes | Lives with the repo. |
env.allow | no | yes | yes | Repo decides what is safe to forward. |
| Per-user SSH key path | yes | no | no | Personal preference. |
aws.region, aws.ami | optional | yes | yes | Repos can pin region. |
| Tailscale tags and template | yes | yes | yes | Both layers can set this. |
| Profiles | yes | yes | n/a | Either layer can define profiles. |
The rule of thumb: anything other repos should inherit when they clone goes in repo config; anything tied to one operator's machine goes in user config.
#Validation
The CLI validates config eagerly:
parseNetworkModerejects--networkvalues outsideauto|tailscale|public;validateNetworkConfigrequirestailscale.tagswhentailscale.enabledvalidateRequestedCapabilitiesrejects--desktop,--browser, orcrabbox doctorruns a richer set of checks against config, network
is true and rejects Tailscale on Blacksmith and static providers;
--code for providers whose Spec.Features does not list the matching feature flag;
reachability, and SSH keys.
When validation fails, crabbox exits with code 2 and a message that names the offending field.
Related docs: