14 views
Your coding agent just spun a full Fargate task on on-demand capacity to clone a repo, run pytest, and apply a 40-line patch — then sat idle for nine minutes waiting for the next tool turn. Multiply that by 80 tenants and your sandbox bill looks like a product P&L. Fargate Spot is the unfair advantage for short-lived clone/test/apply sandboxes: you pay Spot prices for capacity that should die in minutes, not hours. Pair with CodeBuild spec sandboxes when you want managed build images, and with AWS Budgets + Cost Anomaly so Spot savings do not hide a runaway fleet — this post is the ephemeral Spot sandbox layer.
⚡ TL;DR: Prefer Fargate Spot for agent sandboxes that clone → test → apply → exit in under ~15 minutes. Handle SIGTERM (2-minute notice) by checkpointing patch state to S3/DynamoDB, never leaving half-applied trees as source of truth. Keep task roles least-privilege; complement with Organizations SCPs. Related: CodeBuild sandboxes, ECR+Lambda containers, multi-tenant Fargate Spot runtimes.
Why always-on Fargate loses to Spot for agent sandboxes
Coding-agent sandboxes are bursty and discardable:- Clone (or mount) a worktree
- Run lint/tests/typecheck
- Apply a patch or open a PR artifact
- Exit — the next turn may land on a different task
| Approach | Idle cost | Interrupt risk | Best agent shape |
|---|---|---|---|
| Always-on Fargate | ❌ Pay while waiting | ✅ None | Long IDE sessions |
| On-demand Fargate per turn | ✅ No idle | ✅ None | Strict SLAs, short turns |
| Fargate Spot per turn | ✅ Cheapest | ❌ ~2 min SIGTERM | Clone/test/apply <15m |
| CodeBuild on-demand | ✅ Pay per build | ✅ Managed | Spec-driven one-shots |
| Lambda / SnapStart tools | ✅ Cheap | N/A | Sub-minute tools only |
Task definitions that stay Spot-friendly
Keep the task thin. Bake toolchains into the image (ECR container pattern applies mentally even when the runtime is ECS). Pass work via env + S3, not giant command lines.typescript
// ✅ Spot capacity provider strategy for agent sandboxes (CDK)
import * as ecs from "aws-cdk-lib/aws-ecs";
const cluster = new ecs.Cluster(this, "AgentSandboxes", { vpc });
cluster.enableFargateCapacityProviders();
const taskDef = new ecs.FargateTaskDefinition(this, "SandboxTask", {
cpu: 1024,
memoryLimitMiB: 2048,
runtimePlatform: {
cpuArchitecture: ecs.CpuArchitecture.ARM64,
operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
},
});
taskDef.addContainer("sandbox", {
image: ecs.ContainerImage.fromEcrRepository(repo, "agent-sandbox-1.4.2"),
logging: ecs.LogDrivers.awsLogs({ streamPrefix: "agent-sandbox" }),
environment: {
WORK_BUCKET: workBucket.bucketName,
// ❌ Never bake GitHub PATs here — use Secrets Manager (next post)
},
secrets: {
GITHUB_TOKEN: ecs.Secret.fromSecretsManager(ghSecret, "token"),
},
stopTimeout: cdk.Duration.seconds(120), // align with Spot SIGTERM window
});
// RunTask with Spot
await ecs.send(
new RunTaskCommand({
cluster: cluster.clusterName,
taskDefinition: taskDef.taskDefinitionArn,
capacityProviderStrategy: [
{ capacityProvider: "FARGATE_SPOT", weight: 1, base: 0 },
// ✅ optional on-demand fallback for critical tenants
{ capacityProvider: "FARGATE", weight: 0, base: 0 },
],
networkConfiguration: {
awsvpcConfiguration: {
subnets: privateSubnets,
securityGroups: [sandboxSg],
assignPublicIp: "DISABLED",
},
},
overrides: {
containerOverrides: [
{
name: "sandbox",
environment: [
{ name: "TENANT_ID", value: tenantId },
{ name: "TURN_ID", value: turnId },
{ name: "PATCH_S3_URI", value: patchUri },
],
},
],
},
})
);
python
Rule: one Spot task ≈ one tool turn (or one bounded Step Functions branch). Persist artifacts to S3 with conditional writes; orchestrate multi-step graphs with Step Functions.
# ❌ Spot sandbox that treats /workspace as durable source of truth
def apply_and_sleep(repo_dir: str, patch: str):
apply_patch(repo_dir, patch)
# agent waits for next chat turn inside the same task
time.sleep(600) # burning Spot + inviting interruption mid-idle
Interruption handling that coding agents actually need
Fargate Spot sends SIGTERM ≈ two minutes before reclaim. Your entrypoint must:- Stop accepting new tool work
- Flush in-flight patch / test results to S3
- Write a
turn_status=interruptedrow (DynamoDB) so the planner can retry - Exit 0 if checkpoint succeeded (so orchestrators do not double-apply blindly)
bash
❌ Ignoring SIGTERM and relying on ECS to hard-kill leaves half-written trees and duplicate PR comments when Step Functions retries.
#!/usr/bin/env bash
# ✅ entrypoint with Spot-aware drain
set -euo pipefail
CHECKPOINT=""
on_term() {
echo "spot_sigterm turn=$TURN_ID"
if [[ -n "${CHECKPOINT}" ]]; then
aws s3 cp "$CHECKPOINT" "s3://$WORK_BUCKET/tenants/$TENANT_ID/turns/$TURN_ID/checkpoint.tgz"
aws dynamodb update-item \
--table-name AgentTurns \
--key "{\"pk\":{\"S\":\"$TENANT_ID#$TURN_ID\"}}" \
--update-expression "SET #s = :i, updatedAt = :t" \
--expression-attribute-names '{"#s":"status"}' \
--expression-attribute-values "{\":i\":{\"S\":\"interrupted\"},\":t\":{\"S\":\"$(date -u +%FT%TZ)\"}}"
fi
exit 0
}
trap on_term TERM
git clone --depth 1 "$REPO_URL" /work/repo
cd /work/repo
aws s3 cp "$PATCH_S3_URI" /tmp/patch.diff
git apply /tmp/patch.diff
pytest -q | tee /tmp/test.out
tar -czf /tmp/out.tgz /tmp/test.out
CHECKPOINT=/tmp/out.tgz
aws s3 cp /tmp/out.tgz "s3://$WORK_BUCKET/tenants/$TENANT_ID/turns/$TURN_ID/result.tgz"
IAM and blast radius on Spot fleets
Spot makes it cheap to run more tasks. That amplifies bad IAM:- Task role:
s3:GetObject/PutObjectontenants/${tenant}/*only - Deny
iam:*,organizations:*, broadec2:*via SCPs - Authorize which tools may
RunTaskwith Verified Permissions / Cedar - Kill switches via AppConfig when Spot interruption rates spike in a region
json
// ✅ task-role fragment — tenant-prefixed objects only
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::agent-work-prod/tenants/${aws:PrincipalTag/tenant_id}/*"
}
Spot vs CodeBuild vs Batch — pick by duration
- < 60s tools → Lambda (+ SnapStart / containers), not Fargate
- 1–15 min clone/test/apply → Fargate Spot (this post)
- Spec-driven CI image, one-shot → CodeBuild sandboxes
- Hours-long eval / overnight → AWS Batch (Spot compute env OK)
Production checklist
- [ ] Capacity provider prefers
FARGATE_SPOT; on-demand only as explicit fallback - [ ] Task stopTimeout / entrypoint trap handles SIGTERM and checkpoints to S3
- [ ] Turn state machine records
interruptedand retries idempotently - [ ] Secrets from Secrets Manager / SSM — never env baked into image
- [ ] Task role tenant-scoped; SCP + Cedar deny privilege escalation
- [ ] Budgets/anomaly alarms on ECS Fargate Spot usage per tenant OU
- [ ] Packages via CodeArtifact, not public npm during Spot storms
- [ ] Logs/traces tagged
tenant_id,turn_id,capacity_provider=FARGATE_SPOT
FAQ
Q: Will Spot interruptions ruin agent UX?A: Only if a single task owns a multi-minute chat session. Scope tasks to one turn, checkpoint, and let the planner retry — users feel a retry, not a stuck IDE. Q: Can I mix Spot and on-demand in one service?
A: Yes — weight Spot high, keep a base of on-demand for premium tenants. Encode the choice in the orchestrator, not in tribal knowledge. Q: Is this the same as the older “isolated Fargate Spot runtimes” post?
A: That piece covers multi-tenant isolation patterns; this one is specifically cheap ephemeral sandboxes + interruption + task defs for clone/test/apply loops. Fargate Spot turns coding-agent sandboxes from a standing army into a militia: cheap, short-lived, and interruption-aware. Wire SIGTERM to checkpoints, keep IAM boring, and let Step Functions / Batch own anything longer than a turn.
Watch: 100 Days of AI on YouTube
Short videos from CheatCoders, one AI engineering topic per day.
Most viewed
- Python Decorators Explained: From Simple Wrappers to Production Patterns
- Claude Code vs Cursor vs GitHub Copilot: Choose by Workflow, and Set Up One Repo That Works With All Three
- AI Agent Frameworks in 2025: LangGraph vs CrewAI vs AutoGen vs Raw API
- Python String Methods: Every str Method With Real Production Examples
- REST API Design Best Practices: The Patterns That Make APIs a Joy to Use
Newly added
- n8n vs AI Agent: I Gave Both the Same Job (Local, Ollama)
- Local AI Video Generator: Build Your Own App (Free, Python, No GPU)
- AI Wrote Raw Binary Instead of Code. Here’s What Happened (WebAssembly)
- I Built a Browser That Only Runs Encrypted Binary (Ep 1)
- RAG From Scratch: Chunking, Embeddings, Reranking and Evaluation (Real Numbers)
Deep-dive PDF
Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.
Already subscribed? or open the subscribe page.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.