Custom Sandbox Images
Bring your own container image to run agent tasks in a pre-built environment.
Why Use a Custom Image?
By default, the runbook step builds a fresh container from a Dockerfile on every run. This works well for simple tasks, but has drawbacks:
- Slow cold starts -- installing packages on every run wastes time
- Reproducibility -- builds may break if upstream packages change
- Complex toolchains -- some environments need system libraries, compilers, or tools that are tedious to install via
apt_packages
A custom image solves all three. You build it once, push it to a container registry, and reference it by tag. Every run gets the exact same environment in seconds.
How It Works
Set the image parameter to a full container image reference. Jetty pulls the image directly from the registry and launches the sandbox -- no Dockerfile is generated.
{
"activity": "runbook",
"instruction": "Run the test suite and write results to /app/results/report.md",
"agent": "claude-code",
"image": "ghcr.io/myorg/my-test-env:v1.2"
}
The agent (Claude Code, Codex, or Gemini CLI) is installed on top of your image at runtime. Your image provides the base environment; Jetty handles the agent.
Pre-Built Snapshots
Before building a custom image, check if a pre-built Jetty snapshot fits your needs:
| Snapshot | Includes | Best For |
|---|---|---|
python312-uv | Python 3.12, uv package manager, network access | Most tasks: data processing, API calls, code generation, file manipulation |
prism-playwright | Everything in python312-uv + Playwright + Chromium | Browser automation: screenshots, web scraping, OAuth flows, HTML rendering, UI testing |
Use a snapshot by setting the snapshot parameter:
{
"activity": "runbook",
"instruction": "...",
"agent": "claude-code",
"snapshot": "prism-playwright"
}
Or in runbook frontmatter:
---
snapshot: prism-playwright
---
Snapshots load faster than custom images and are maintained by Jetty. Only build a custom image when you need packages or tools not available in either snapshot.
Supported Registries
Any public container registry works:
| Registry | Example |
|---|---|
| Docker Hub | docker.io/myuser/my-image:latest or myuser/my-image:latest |
| GitHub Container Registry | ghcr.io/myorg/my-image:v1 |
| Google Artifact Registry | us-docker.pkg.dev/my-project/repo/image:tag |
| Amazon ECR Public | public.ecr.aws/myorg/image:tag |
Images must be publicly accessible. Private registry authentication is not currently supported.
Building Your Image
Image Requirements
- Linux/amd64 architecture
- bash must be available (most base images include it)
- Network access during build is fine -- the sandbox itself can optionally disable network at runtime via
network_enabled: false - Write output files to
/app/results/for automatic artifact persistence
Example: Data Science Environment
FROM python:3.13-slim-bookworm
RUN apt-get update && apt-get install -y \
git curl build-essential libpq-dev \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir \
pandas numpy scipy scikit-learn \
matplotlib seaborn plotly \
jupyter nbformat \
sqlalchemy psycopg2-binary
WORKDIR /app
Build and push:
docker build -t ghcr.io/myorg/data-science:v1 .
docker push ghcr.io/myorg/data-science:v1
Then use it in a workflow:
{
"activity": "runbook",
"instruction": "Analyze the uploaded CSV. Produce charts and a summary report in /app/results/.",
"agent": "claude-code",
"image": "ghcr.io/myorg/data-science:v1",
"files": ["uploads/dataset.csv"]
}
Example: Browser Automation
FROM mcr.microsoft.com/playwright:v1.52.0-noble
RUN npm install -g playwright @playwright/test
RUN npx playwright install --with-deps chromium
WORKDIR /app
{
"activity": "runbook",
"instruction": "Navigate to https://example.com, take screenshots of each page, and write a visual QA report to /app/results/.",
"agent": "claude-code",
"image": "ghcr.io/myorg/playwright-env:v1",
"timeout_sec": 1200
}
Chat Completions API
Use the image field inside the jetty block:
curl -X POST "https://flows-api.jetty.io/v1/chat/completions" \
-H "Authorization: Bearer $JETTY_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"messages": [
{
"role": "system",
"content": "You are a data scientist. Analyze the uploaded dataset and produce a report."
},
{
"role": "user",
"content": "Run the analysis"
}
],
"stream": true,
"jetty": {
"runbook": true,
"collection": "my-org",
"task": "analysis",
"agent": "claude-code",
"image": "ghcr.io/myorg/data-science:v1",
"file_paths": ["uploads/dataset.csv"]
}
}'
Choosing the Right Sandbox Source
| Option | Parameter | Best for | Trade-off |
|---|---|---|---|
| Dockerfile build | base_image + pip_packages + apt_packages | Simple environments, quick iteration | Slower startup (builds every run) |
| Custom image | image | Complex environments, reproducible builds | You maintain the image |
| Daytona snapshot | snapshot | Jetty-managed pre-built environments | Must be pre-created on the platform |
Priority when multiple are set: snapshot > image > Dockerfile build.
Tips
- Tag your images with specific versions (
v1.2, notlatest) for reproducibility - Keep images small -- use slim base images and multi-stage builds to reduce pull time
- Pre-install large packages (ML frameworks, browser engines) in the image to avoid per-run installation
- Test locally before pushing:
docker run -it your-image bashto verify the environment works - The
pip_packagesandapt_packagesparameters are ignored whenimageis set -- bake everything into your image instead