dlo is a deterministic Docker deployment optimizer built for agents and long-lived projects. It learns how a repository changes, identifies expensive cache-invalidating Dockerfile layers, generates safer layouts, and automatically applies a candidate only after paired benchmarks and project-specific correctness checks prove it better.
The engine is a native Go binary. It parses Dockerfiles with the canonical Moby BuildKit Dockerfile parser and typed instruction decoder, rather than maintaining a second Dockerfile grammar. Wendy is supported but not required.
Wrapping a build does not itself make Docker faster. The speedup comes from a concrete Dockerfile or context change that DLO identifies, proves, and applies.
Download a static binary from GitHub Releases, or install from source with Go 1.26.3+:
go install github.com/wendylabsinc/docker-layer-optimizer/cmd/dlo@latest
dlo --versionStatic analysis requires Git. Measured builds and optimization proofs also require Docker with Buildx.
Planning is read-only and returns evidence, a stable candidate ID, risks, and a unified diff:
dlo optimize --root . --plan --jsonConfigure the representative edit and correctness contract once:
# .dlo.yml
version: 1
verification:
commands:
- go test ./...
benchmark:
source_path: internal/app.go
trials: 3
budget_seconds: 600
min_relative_improvement: 0.10
min_absolute_seconds: 0.5
max_relative_regression: 0.10
max_absolute_regression_seconds: 0.5
payback_deploys: 20Then benchmark and, if every proof gate passes, apply the built-in candidate:
dlo optimize --root . --jsonDLO creates disposable control and candidate snapshots, warms both builds, performs paired source edits, checks no-op and dependency-edit regressions, runs the configured commands, and estimates payback. The real working tree is changed only when every gate passes and the affected files still match their pre-verification hashes.
Agents can submit any Docker-related unified diff through the same proof engine:
dlo optimize --root . --candidate /tmp/candidate.patch --plan --json
dlo optimize --root . --candidate /tmp/candidate.patch --jsonBase images, dependency versions, entrypoints, users, ports, health checks, privileges, architecture, and other protected behavior are never auto-applied. An explicitly reviewed plan can be applied without a performance proof only by its exact ID using --apply-approved ID.
dlo build --root /path/to/project --tag my-app:devThe result separates cached and rebuilt BuildKit steps, context transfer, image layer DiffID changes, build duration, and DLO observer overhead. BuildKit rawjson is preferred because it carries an explicit cache flag; plain progress is available as a fallback.
For a pushed image, compare compressed OCI registry blobs:
dlo build --root . --tag registry.example.com/team/app:dev --pushunmatched_compressed_bytes is the size of current blobs absent from the previous observed manifest, not a claim about actual network bytes or registry billing.
Common Buildx settings pass through directly:
dlo build --root . --dockerfile docker/Dockerfile --tag my-app:dev \
--platform linux/amd64 --target runtime --build-arg VERSION=devWrap the actual deployment command so build, transfer, replacement, and readiness are measured separately:
dlo deploy --root . --target woof -- wendy --device woof.local run --detach --yes
dlo deploy --root . --target staging -- docker compose up --build -d --waitFor wendy run, DLO enables Wendy timing output and inserts --chunking force unless a chunking mode was already chosen. This keeps an expensive registry fallback from silently hiding a verified layer-diff advantage. Wendy and Compose are auto-detected; other systems can define output markers:
dlo deploy --root . --adapter generic \
--phase-marker 'build=^Compiling' \
--phase-marker 'readiness=^Service ready$' \
-- ./deploy.shCommands and output logs are never persisted.
dlo analyze --root /path/to/project
dlo analyze --root /path/to/project --json
dlo history --root /path/to/project --jsonThe analyzer maps COPY and ADD through BuildKit's parsed instruction model, respects Docker ignore rules, combines recency-weighted Git history with local observations, and ranks invalidation points by change likelihood × downstream comparative cost. A relevant non-build task can be recorded without storing its description:
dlo record --root . --kind task --status success --from-git --tag dependenciesObservations and short-lived proof records live outside the repository in the operating system user cache:
- macOS:
~/Library/Caches/docker-layer-optimizer/ - Linux:
${XDG_CACHE_HOME:-~/.cache}/docker-layer-optimizer/ - Windows:
%LOCALAPPDATA%/docker-layer-optimizer/
Set DLO_CACHE_DIR to override the base directory. Successful proofs are retained for at most 30 days and 20 runs; failed proofs expire after seven days. DLO stores aggregate measurements, paths and hashes, IDs, coarse tags, and phase timings. It does not persist patches, commands, logs, source contents, prompts, secrets, environment values, or build-argument values.
See SECURITY.md and the observation schema.
Codex or Claude supplies project judgment; DLO supplies deterministic measurement and application gates.
codex plugin marketplace add wendylabsinc/docker-layer-optimizer --ref mainInside Codex, open /plugins, install Docker Layer Optimizer, and begin a new session. A useful first request is:
Use optimize-docker-layers to observe this project's normal Docker workflow, explain the dominant bottleneck, and plan a measured optimization. Do not apply an unverified patch.
Claude Code:
claude plugin marketplace add wendylabsinc/docker-layer-optimizer
claude plugin install docker-layer-optimizer@docker-optimization-toolsThe plugin is optional; agents can call the JSON CLI directly.
| Capability | Requirement | Notes |
|---|---|---|
| Static analysis | Native dlo, Git |
Docker is optional. |
| Dockerfile parsing | Moby BuildKit parser | Supports stages, flags, JSON form, continuations, heredocs, and COPY --from. |
| Structured step counts | Docker Buildx with --progress=rawjson |
Plain fallback is less robust. |
| Local layer comparison | Successful --load exporter |
Compares uncompressed DiffIDs. |
| Registry comparison | Readable pushed OCI/Docker manifest | Compares compressed blob digests and declared sizes. |
| Deployment profiling | Recognizable or custom output markers | Wendy and Compose adapters included. |
| Platforms | Linux, macOS, Windows; AMD64 and ARM64 | Static release binaries; CI cross-compiles all six targets. |
go test ./...
go vet ./...
go build ./cmd/dloReal-Docker lifecycle tests are opt-in:
DLO_DOCKER_INTEGRATION=1 go test ./internal/integration -v -count=1 -timeout=20mThe historical benchmark harness uses Python only as test orchestration; the DLO runtime is entirely Go:
python3 benchmarks/run_benchmarks.py --iterations 5 --output benchmark.jsonResults are synthetic or project-specific, not universal performance claims. See the methodology, Colima ARM64 result, Woof end-to-end benchmark, and agent-first proof.
MIT