Repo Configuration
When Bucky runs a session in your repo, it checks for a .bucky/config.yaml file in the repo root. This file lets you customize the session pod environment — Docker image, lifecycle hooks, environment variables, secrets, resource limits, and timeouts.
The file is optional. Repos without it use sensible defaults.
Quick start
Section titled “Quick start”The bucky CLI creates a starter config when you initialize a repo:
bucky fetch-rewards/my-serviceOr create .bucky/config.yaml manually:
# yaml-language-server: $schema=https://bucky-docs.fetchrewards.com/schemas/config.v1.jsonversion: 1Schema
Section titled “Schema”The full JSON schema is available at bucky-docs.fetchrewards.com/schemas/config.v1.json. Adding the yaml-language-server comment at the top of the file enables autocomplete and validation in VS Code and other editors.
Field reference
Section titled “Field reference”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
version | integer | Yes | — | Schema version. Must be 1. Required in every config file. |
image | string | No | Bucky base image | Custom Docker image URI. Must be from an approved ECR registry and must extend the Bucky base image. |
resources.requests.cpu | string | No | "1" | CPU request (Kubernetes quantity). |
resources.requests.memory | string | No | "4Gi" | Memory request (Kubernetes quantity). |
resources.limits.cpu | string | No | "2" | CPU limit. Server-enforced max: "4". |
resources.limits.memory | string | No | "8Gi" | Memory limit. Server-enforced max: "16Gi". |
timeout | string | No | "1h" | Session timeout in Go duration format (30m, 1h, 2h30m). Max: "4h". |
hooks.setup | string | No | — | Path to a setup script, relative to the repo root. Runs after clone, before Claude starts. |
hooks.teardown | string | No | — | Path to a teardown script, relative to the repo root. Runs after Claude finishes, before state is saved to S3. |
env | object | No | — | Key-value pairs injected as environment variables. Plain strings are injected as-is. Values prefixed with sm:// are resolved from AWS Secrets Manager at dispatch time. |
Environment variables
Section titled “Environment variables”Plain string values are injected directly into the pod:
version: 1env: DATABASE_URL: "postgres://localhost:5432/mydb" RUST_LOG: "debug"Use plain env values for non-sensitive configuration: log levels, feature flags, service URLs on internal networks, database connection strings for local databases running inside the pod.
Secrets Manager references
Section titled “Secrets Manager references”Prefix a value with sm:// to resolve it from AWS Secrets Manager at dispatch time. The agent fetches the secret using its IAM role and injects the resolved value into the pod securely (via Kubernetes Secret, never as a plain env var).
version: 1env: MY_API_KEY: "sm://my-team/api-key" DD_API_KEY: "sm://arn:aws:secretsmanager:us-east-1:123456789:secret:datadog-key"Both short names (sm://my-team/api-key) and full ARNs (sm://arn:aws:...) are supported.
How sm:// resolution works:
- The agent parses the config before launching the pod.
- For each
sm://value, it callssecretsmanager:GetSecretValueusing the agent’s IAM role. - The agent’s IAM policy scopes access to an allowed prefix — your team can only access secrets under paths you have been granted.
- Resolved values are mounted into the pod as a Kubernetes Secret object, not as plaintext in the pod spec.
- If any secret reference is invalid, not found, or unauthorized, the session fails immediately with a descriptive error. Claude never starts.
When to use sm:// vs plain values:
Use sm:// for | Use plain values for |
|---|---|
| API keys and tokens | Log levels (RUST_LOG, LOG_LEVEL) |
| Database passwords | Non-sensitive URLs (internal service endpoints) |
| Third-party credentials (Datadog, PagerDuty) | Feature flags |
| Anything you would not put in a Git repo | Build configuration (NODE_ENV, GO_ENV) |
Lifecycle hooks
Section titled “Lifecycle hooks”Hooks are scripts in your repo that run at specific points in the pod lifecycle. Paths are relative to the repo root.
version: 1hooks: setup: ".bucky/setup.sh" teardown: ".bucky/teardown.sh"| Hook | When it runs | Use cases |
|---|---|---|
setup | After repo clone, before Claude starts | Install dependencies, start local services, run migrations, configure tools |
teardown | After Claude finishes, before state is saved | Stop services, clean up temp files |
Setup hook examples
Section titled “Setup hook examples”Node.js / TypeScript service
Section titled “Node.js / TypeScript service”#!/usr/bin/env bashset -euo pipefail
# Install dependenciespnpm install --frozen-lockfile
# Start local databasedocker compose up -d postgressleep 3
# Run migrationspnpm db:migrateGo service
Section titled “Go service”#!/usr/bin/env bashset -euo pipefail
# Download Go modules (cached across sessions if using a custom image)go mod download
# Start dependent servicesdocker compose up -d postgres redissleep 5
# Run database migrationsgo run ./cmd/migrate up
# Verify the service compilesgo build ./...Python service
Section titled “Python service”#!/usr/bin/env bashset -euo pipefail
# Install Python via mise (available in the base image)mise use -g python@3.12
# Create virtualenv and install dependenciespython -m venv .venvsource .venv/bin/activatepip install -r requirements.txt
# Start database and run migrationsdocker compose up -d postgressleep 3python manage.py migrateService with Docker Compose dependencies
Section titled “Service with Docker Compose dependencies”#!/usr/bin/env bashset -euo pipefail
# Start the full dependency stackdocker compose up -d postgres redis elasticsearch localstackecho "Waiting for services to be healthy..."
# Wait for each service to be readydocker compose exec -T postgres pg_isready -U postgres --timeout=30docker compose exec -T redis redis-cli pingcurl -sf --retry 10 --retry-delay 2 http://localhost:9200/_cluster/health
# Install app dependencies and seed datapnpm install --frozen-lockfilepnpm db:migratepnpm db:seedCustom Docker image
Section titled “Custom Docker image”For teams that need OS-level packages, GPU drivers, or a custom toolchain beyond what a setup script can provide:
version: 1image: "292095839402.dkr.ecr.us-east-1.amazonaws.com/my-team/session:latest"Building a custom image
Section titled “Building a custom image”Custom images must extend the Bucky base image, which includes all the runtime scaffolding (scripts, plugins, prompts, Claude CLI, git, tmux, gh, aws CLI, mise). Your Dockerfile only needs to add your team’s languages and packages:
FROM 292095839402.dkr.ecr.us-east-1.amazonaws.com/buck-bronson-claude-base:latest
# Install your runtime via mise (already available in the image)RUN mise use -g python@3.12
# Or install system packagesRUN apt-get update && apt-get install -y --no-install-recommends \ libpq-dev \ && rm -rf /var/lib/apt/lists/*The base image provides:
| Included in base image |
|---|
Entrypoint + lifecycle scripts (entrypoint.sh, on-stop.sh, etc.) |
| Claude Code CLI |
| MCP plugins (Sourcegraph, Grafana, Rollbar, PostHog, Playwright) |
| System prompts |
| git, tmux, jq, curl, gh CLI, AWS CLI, mise |
When to use a custom image vs a setup hook
Section titled “When to use a custom image vs a setup hook”Most teams should start with a setup hook and only move to a custom image when they hit a limitation. Here is a quick decision guide:
Use a setup hook when you need to:
- Install language dependencies (
pnpm install,go mod download,pip install) - Start services with Docker Compose
- Run database migrations or seed scripts
- Configure tools or generate files
- Anything that runs in under a few minutes and only needs userspace tools
Use a custom image when you need to:
- Install OS-level packages (
apt-get install) that require root - Add custom runtimes or compilers not available through mise
- Install GPU drivers or CUDA toolkits
- Set up toolchains that take too long to install at session start (10+ minutes)
- Pre-bake large datasets or model files into the image
Resource overrides
Section titled “Resource overrides”Override CPU and memory requests/limits for pods that need more (or less) than the default:
version: 1resources: requests: cpu: "2" memory: "8Gi" limits: cpu: "4" memory: "16Gi"Defaults are 1 CPU / 4Gi requests, 2 CPU / 8Gi limits. Server-enforced maximums are 4 CPU and 16Gi.
You can override requests and limits independently. For example, to only increase the memory limit while keeping everything else at defaults:
version: 1resources: limits: memory: "12Gi"Timeout
Section titled “Timeout”Override the pod timeout (default 1 hour, maximum 4 hours):
version: 1timeout: "2h"Uses Go duration format: 30m, 1h, 1h30m, 2h.
Validation and error handling
Section titled “Validation and error handling”Bucky validates the config file before launching a session pod. If validation fails, the session never starts and you get a clear error in the dashboard and in the Slack/Jira response.
Here is what happens for common problems:
| Problem | What happens |
|---|---|
version is missing or not 1 | Config rejected. Session does not start. |
resources exceed server maximums (4 CPU / 16Gi) | Config rejected. Session does not start. Reduce your values to be within the allowed range. |
sm:// secret reference is unauthorized or not found | Session fails at dispatch time with a descriptive error naming the secret. Claude never starts. |
| Hook script path does not exist in the repo | Session fails before Claude starts. Check that the path is relative to the repo root and the file is committed. |
| Hook script is not executable | Session fails before Claude starts. Run chmod +x on the script and commit the change. |
image is not from an approved ECR registry | Config rejected. Session does not start. Only images from the Fetch ECR registry are allowed. |
image does not extend the Bucky base image | Pod starts but may fail unpredictably. Always use FROM buck-bronson-claude-base in your Dockerfile. |
timeout exceeds 4h | Config rejected. Session does not start. |
timeout is not valid Go duration format | Config rejected. Session does not start. Use formats like 30m, 1h, 2h30m. |
| YAML syntax error | Config rejected. Session does not start. Use the JSON schema comment for editor validation. |
Full example
Section titled “Full example”# yaml-language-server: $schema=https://bucky-docs.fetchrewards.com/schemas/config.v1.jsonversion: 1
image: "292095839402.dkr.ecr.us-east-1.amazonaws.com/my-team/session:latest"
resources: requests: cpu: "2" memory: "8Gi" limits: cpu: "4" memory: "16Gi"
timeout: "2h"
hooks: setup: ".bucky/setup.sh" teardown: ".bucky/teardown.sh"
env: DATABASE_URL: "postgres://localhost:5432/mydb" RUST_LOG: "debug" MY_API_KEY: "sm://my-team/api-key"Multi-repo sessions
Section titled “Multi-repo sessions”Per-repo config only applies to single-repo sessions. Multi-repo sessions always use the default runner with no config overrides, since there’s no single repo to take config from.