Skip to content

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.

The bucky CLI creates a starter config when you initialize a repo:

Terminal window
bucky fetch-rewards/my-service

Or create .bucky/config.yaml manually:

# yaml-language-server: $schema=https://bucky-docs.fetchrewards.com/schemas/config.v1.json
version: 1

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.

FieldTypeRequiredDefaultDescription
versionintegerYesSchema version. Must be 1. Required in every config file.
imagestringNoBucky base imageCustom Docker image URI. Must be from an approved ECR registry and must extend the Bucky base image.
resources.requests.cpustringNo"1"CPU request (Kubernetes quantity).
resources.requests.memorystringNo"4Gi"Memory request (Kubernetes quantity).
resources.limits.cpustringNo"2"CPU limit. Server-enforced max: "4".
resources.limits.memorystringNo"8Gi"Memory limit. Server-enforced max: "16Gi".
timeoutstringNo"1h"Session timeout in Go duration format (30m, 1h, 2h30m). Max: "4h".
hooks.setupstringNoPath to a setup script, relative to the repo root. Runs after clone, before Claude starts.
hooks.teardownstringNoPath to a teardown script, relative to the repo root. Runs after Claude finishes, before state is saved to S3.
envobjectNoKey-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.

Plain string values are injected directly into the pod:

version: 1
env:
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.

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: 1
env:
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:

  1. The agent parses the config before launching the pod.
  2. For each sm:// value, it calls secretsmanager:GetSecretValue using the agent’s IAM role.
  3. The agent’s IAM policy scopes access to an allowed prefix — your team can only access secrets under paths you have been granted.
  4. Resolved values are mounted into the pod as a Kubernetes Secret object, not as plaintext in the pod spec.
  5. 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:// forUse plain values for
API keys and tokensLog levels (RUST_LOG, LOG_LEVEL)
Database passwordsNon-sensitive URLs (internal service endpoints)
Third-party credentials (Datadog, PagerDuty)Feature flags
Anything you would not put in a Git repoBuild configuration (NODE_ENV, GO_ENV)

Hooks are scripts in your repo that run at specific points in the pod lifecycle. Paths are relative to the repo root.

version: 1
hooks:
setup: ".bucky/setup.sh"
teardown: ".bucky/teardown.sh"
HookWhen it runsUse cases
setupAfter repo clone, before Claude startsInstall dependencies, start local services, run migrations, configure tools
teardownAfter Claude finishes, before state is savedStop services, clean up temp files
#!/usr/bin/env bash
set -euo pipefail
# Install dependencies
pnpm install --frozen-lockfile
# Start local database
docker compose up -d postgres
sleep 3
# Run migrations
pnpm db:migrate
#!/usr/bin/env bash
set -euo pipefail
# Download Go modules (cached across sessions if using a custom image)
go mod download
# Start dependent services
docker compose up -d postgres redis
sleep 5
# Run database migrations
go run ./cmd/migrate up
# Verify the service compiles
go build ./...
#!/usr/bin/env bash
set -euo pipefail
# Install Python via mise (available in the base image)
mise use -g python@3.12
# Create virtualenv and install dependencies
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Start database and run migrations
docker compose up -d postgres
sleep 3
python manage.py migrate
#!/usr/bin/env bash
set -euo pipefail
# Start the full dependency stack
docker compose up -d postgres redis elasticsearch localstack
echo "Waiting for services to be healthy..."
# Wait for each service to be ready
docker compose exec -T postgres pg_isready -U postgres --timeout=30
docker compose exec -T redis redis-cli ping
curl -sf --retry 10 --retry-delay 2 http://localhost:9200/_cluster/health
# Install app dependencies and seed data
pnpm install --frozen-lockfile
pnpm db:migrate
pnpm db:seed

For teams that need OS-level packages, GPU drivers, or a custom toolchain beyond what a setup script can provide:

version: 1
image: "292095839402.dkr.ecr.us-east-1.amazonaws.com/my-team/session:latest"

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 packages
RUN 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

Override CPU and memory requests/limits for pods that need more (or less) than the default:

version: 1
resources:
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: 1
resources:
limits:
memory: "12Gi"

Override the pod timeout (default 1 hour, maximum 4 hours):

version: 1
timeout: "2h"

Uses Go duration format: 30m, 1h, 1h30m, 2h.

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:

ProblemWhat happens
version is missing or not 1Config 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 foundSession fails at dispatch time with a descriptive error naming the secret. Claude never starts.
Hook script path does not exist in the repoSession fails before Claude starts. Check that the path is relative to the repo root and the file is committed.
Hook script is not executableSession fails before Claude starts. Run chmod +x on the script and commit the change.
image is not from an approved ECR registryConfig rejected. Session does not start. Only images from the Fetch ECR registry are allowed.
image does not extend the Bucky base imagePod starts but may fail unpredictably. Always use FROM buck-bronson-claude-base in your Dockerfile.
timeout exceeds 4hConfig rejected. Session does not start.
timeout is not valid Go duration formatConfig rejected. Session does not start. Use formats like 30m, 1h, 2h30m.
YAML syntax errorConfig rejected. Session does not start. Use the JSON schema comment for editor validation.
# yaml-language-server: $schema=https://bucky-docs.fetchrewards.com/schemas/config.v1.json
version: 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"

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.