Skip to main content

Budget-Controlled Batch Jobs

Run multiple agent tasks in parallel with per-sandbox budgets, automatic shutdown on overspend, and cost tracking across the batch.

Prerequisites

  • Caged CLI installed
  • API key configured (caged login)
  • Agent API key in Caged secrets

Single Task with Budget Guard

# Run with a $5 hard limit — sandbox terminates if exceeded
caged run \
  --template python-312 \
  --cpus 2 \
  --memory 1024 \
  --repo https://github.com/your-org/data-pipeline \
  --budget 5 \
  --env "OPENAI_API_KEY=$OPENAI_API_KEY"
If the agent hits the $5 limit, the sandbox is immediately terminated and you’ll see:
WARN: Sandbox cage-a1b2c3d4 terminated — budget limit reached ($5.00/$5.00)

Batch Processing Script

Process multiple repos or tasks with individual budgets:
#!/bin/bash
# batch-agent.sh — Run agent tasks in parallel with budget controls

TASKS=(
  "repo1|Fix all type errors|3"
  "repo2|Add unit tests for utils/|5"
  "repo3|Refactor database layer|8"
  "repo4|Update dependencies and fix breaking changes|4"
)

SANDBOX_IDS=()

# Launch all sandboxes
for task in "${TASKS[@]}"; do
  IFS='|' read -r repo prompt budget <<< "$task"

  SANDBOX_ID=$(caged run \
    --template node-20 \
    --cpus 2 \
    --memory 2048 \
    --repo "https://github.com/your-org/$repo" \
    --budget "$budget" \
    --env "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" \
    --json | jq -r '.id')

  echo "[$repo] Sandbox: $SANDBOX_ID (budget: \$$budget)"
  SANDBOX_IDS+=("$SANDBOX_ID|$repo|$prompt")
done

# Run agent in each sandbox
for entry in "${SANDBOX_IDS[@]}"; do
  IFS='|' read -r id repo prompt <<< "$entry"
  echo "[$repo] Running: $prompt"
  caged exec "$id" "claude '$prompt'" &
done

# Wait for all background jobs
wait

# Report results
echo ""
echo "=== Batch Results ==="
caged list --json | jq -r '.[] | "\(.id)\t\(.status)\t$\(.cost)/\(.budget)"'

# Cleanup
for entry in "${SANDBOX_IDS[@]}"; do
  IFS='|' read -r id _ _ <<< "$entry"
  caged destroy "$id" --force
done

API-Driven Batch with Webhooks

For production batch processing, use the API with webhook callbacks:
#!/bin/bash
# Create sandbox with completion webhook
curl -X POST https://api.caged.dev/v1/sandboxes \
  -H "Authorization: Bearer caged_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "template": "node-20",
    "resources": {"cpu": 2, "memory": 2048},
    "repo": "https://github.com/your-org/project",
    "budget": 5.00,
    "secrets": ["ANTHROPIC_API_KEY"],
    "init_script": "npm install && npm install -g @anthropic-ai/claude-code",
    "on_complete": "https://your-server.com/webhook/sandbox-done",
    "on_budget_exceeded": "https://your-server.com/webhook/budget-alert"
  }'

Cost Tracking

Monitor total batch spend:
# List all running sandboxes with costs
caged list

# Output:
# ID               TEMPLATE    STATUS     COST      BUDGET    UPTIME
# cage-a1b2c3d4    node-20     running    $1.23     $3.00     8m
# cage-e5f6g7h8    node-20     running    $2.87     $5.00     12m
# cage-i9j0k1l2    node-20     completed  $3.50     $8.00     22m
# cage-m3n4o5p6    node-20     killed     $4.00     $4.00     15m  ← budget hit

# Total spend across all sandboxes
caged list --json | jq '[.[].cost] | add | "Total: $\(.)"'

Config for Batch Workers

# .caged.yaml for batch processing
template: node-20
resources:
  cpu: 2
  memory: 2048
  disk: 5
timeout: 900           # 15 min idle timeout (aggressive for batch)
budget: 5.00

secrets:
  - ANTHROPIC_API_KEY

network_mode: allowlist
allowed_hosts:
  - api.anthropic.com
  - registry.npmjs.org
  - github.com

env:
  CI: "true"            # Many tools respect this for non-interactive mode
  FORCE_COLOR: "0"      # Clean output for logs

Tips

Set budget per complexity: Simple tasks (lint fixes) need 23.Complexrefactorsneed2-3. Complex refactors need 5-10. Set budgets based on expected token usage.
Use aggressive timeouts: Batch jobs shouldn’t idle. Set timeout: 900 (15 min) so sleeping sandboxes don’t accumulate compute cost.
Monitor with webhooks: For production batch systems, use the on_complete and on_budget_exceeded webhooks instead of polling.