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 insandbox/docker.py:120-384.
Class-Level State
_standby_idis a class variable shared across allDockerSandboxinstances in the same Python process_lockserializes access to the shared standby stateatexithandler ensures containers aren’t destroyed on process exit (they’re reused)
Boot Sequence
Theboot() method implements a three-tier container lookup strategy:
1. Disk Persistence (sandbox/docker.py:152-175)
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:
/Users/you/.ftl/containers/a3f8b2c1d4e5
2. In-Process Standby (sandbox/docker.py:177-182)
ftl shell) without hitting the filesystem between tasks.
3. Create Fresh (sandbox/docker.py:184-190)
sandbox/docker.py:301-318):
- 2GB memory
- 2 CPU cores
- Snapshots mounted read-only at
/mnt/snapshots host.docker.internalresolves 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):
/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:
/tmp/.ftl_env contents:
Setup Command Execution
Runs once on fresh containers only (sandbox/docker.py:208-211):
sandbox/docker.py:328-337):
/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:sandbox/docker.py:339-349):
Command Execution
Non-Streaming Execution
Streaming Execution
- 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
- 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):
Diff Execution
Standby and Cleanup
Standby (sandbox/docker.py:285-290)
- 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 codeinvocation
Exit Cleanup (sandbox/docker.py:375-383)
docker rm -f.
Manual Destroy
Container State Tracking
Image Validation
boot() to fail fast if Docker isn’t ready.
Performance Characteristics
Security Properties
- Filesystem isolation — Agent sees only
/workspaceand system paths - Read-only snapshots —
/mnt/snapshotsmounted read-only - Shadow credentials — Real secrets never written to container env
- Network isolation — Can route through proxy for credential swapping
- 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