Skip to main content
The Docker sandbox isolates agent execution from the host filesystem while providing persistent container reuse to eliminate cold-boot overhead.

Container Lifecycle

From the README (README.md:382-397):
Persistent containers mean zero cold-boot overhead after the first task. Setup commands run once; dependencies persist for the lifetime of the container.

DockerSandbox Class

The core implementation lives in sandbox/docker.py:120-384.

Class-Level State

Design notes:
  • _standby_id is a class variable shared across all DockerSandbox instances in the same Python process
  • _lock serializes access to the shared standby state
  • atexit handler ensures containers aren’t destroyed on process exit (they’re reused)

Boot Sequence

The boot() method implements a three-tier container lookup strategy:

1. Disk Persistence (sandbox/docker.py:152-175)

Why file locking? Two concurrent ftl code invocations for the same project could race to claim the same container. The lock ensures only one session acquires it. Container file path:
Example: /Users/you/.ftl/containers/a3f8b2c1d4e5

2. In-Process Standby (sandbox/docker.py:177-182)

This enables warm reuse in interactive mode (ftl shell) without hitting the filesystem between tasks.

3. Create Fresh (sandbox/docker.py:184-190)

Fresh container creation (sandbox/docker.py:301-318):
Resource limits:
  • 2GB memory
  • 2 CPU cores
  • Snapshots mounted read-only at /mnt/snapshots
  • host.docker.internal resolves to the host gateway (enables proxy routing)
The read-only snapshot mount prevents accidental modification of the baseline state.

Workspace Initialization

Reset workspace from snapshot (sandbox/docker.py:351-365):
Why wipe? On warm container reuse, /workspace contains files from the previous task. We wipe it before copying the new snapshot. Ownership reset: The ftl user (UID 1000) owns all files in /workspace.

Environment Variable Injection

All credentials and agent auth keys are written to /tmp/.ftl_env:
Example /tmp/.ftl_env contents:
Shadow credentials and real agent auth keys coexist — the agent’s API key is real, but project secrets are fake.

Setup Command Execution

Runs once on fresh containers only (sandbox/docker.py:208-211):
Implementation (sandbox/docker.py:328-337):
Setup has a 5-minute timeout. Installed packages persist in /home/ftl/.local/ for the container’s lifetime.

Node.js Pre-Warming

Claude Code’s first invocation has a 5-8s cold-start penalty as Node.js loads modules. FTL pre-warms the cache in the background:
Implementation (sandbox/docker.py:339-349):
By the time the user sees “Running agent…”, Claude Code is already warm.

Command Execution

Non-Streaming Execution

Automatic env sourcing:
Every command gets shadow credentials and agent auth keys without explicit sourcing.

Streaming Execution

Key behaviors:
  • Output streams line-by-line immediately
  • Ctrl+C terminates the container process
  • Full output accumulated for return value
  • No timeout enforcement (caller controls via thread/future)

Root Execution

Used for:
  • Workspace initialization (chown -R ftl:ftl /workspace)
  • Proxy CA installation
  • System-level setup

Diff Computation

Diffs run inside the Linux container to avoid host-side Python walking through VirtioFS per file.

Linux-Internal Diff Script

The sandbox generates a Python script and runs it inside the container: Template (sandbox/docker.py:22-83):
Why base64? Binary file contents can contain characters that break JSON. Base64 ensures safe transport.

Diff Execution

Performance: On a 1000-file project, Linux-internal diff completes in ~500ms vs. 3-5s for host-side Python walking through Docker Desktop’s VirtioFS.

Standby and Cleanup

Standby (sandbox/docker.py:285-290)

The container:
  • Continues running in the background
  • Is marked as available in class-level state
  • Has its ID persisted to disk
  • Will be reused by the next ftl code invocation

Exit Cleanup (sandbox/docker.py:375-383)

Containers are never destroyed on normal exit. They persist until explicitly removed with docker rm -f.

Manual Destroy

Not called during normal operation — only for explicit cleanup commands.

Container State Tracking

Checks if a container ID is still running. Used during boot to validate persisted container references.

Image Validation

Called at the start of every boot() to fail fast if Docker isn’t ready.

Performance Characteristics

Security Properties

  1. Filesystem isolation — Agent sees only /workspace and system paths
  2. Read-only snapshots — /mnt/snapshots mounted read-only
  3. Shadow credentials — Real secrets never written to container env
  4. Network isolation — Can route through proxy for credential swapping
  5. Resource limits — 2GB RAM, 2 CPU cores prevent runaway processes

Next Steps

Session Orchestration

How the sandbox integrates into the session lifecycle

Agent Adapters

How agents execute commands inside the sandbox