Problem
Every project currently builds the entire aidc image from mcr.microsoft.com/devcontainers/base:ubuntu-24.04 up, re-installing the same ~2.5–3 GB of common tooling (base features, apt packages, uv+Python, all 5 coding agents, all scanners) per project, every time a project image is (re)built.
Measured: with zero toolchains, the image is already 3.22 GB. 10 projects = ~32 GB of images with ~30 GB of it identical content.
What's shared today
Only the base image itself (devcontainers/base:ubuntu-24.04, pinned by digest) is shared across project images via Docker layer dedup — because it's pulled once and every project builds on the same base layer. Everything after FROM (apt, uv, agents, scanners, toolchains) is rebuilt per project. In practice the layer content is mostly identical across projects, so Docker can dedup — but only if the build happens at the same time with the same unpinned installer results; curl | bash agent installers fetch "latest" at build time, so layer digests drift and the build cache (45.5 GB on this host!) grows instead of being shared.
Proposal: prebuilt aidc-base image
- Split the Dockerfile:
Dockerfile.base — everything common: base, apt deps, uv+Python, agents, pmg/vet/rtk, semgrep/gitleaks/trufflehog, clipboard bridge, bootstrap-claude script, PATH wiring. No per-project args except AIDC_AGENTS.
- Per-project
Dockerfile becomes: FROM aidc-base:<pinned-version> + toolchain layer + project-setup.sh. Tiny, fast, and identical across projects.
- Build
aidc-base once (tag: aidc-base:vX.Y or digest-pinned), and have aidc up pull it if missing — don't rebuild it per project.
- In
compose.yaml, build context for the per-project image already exists; just change the Dockerfile to reference the shared base.
Result:
- One copy of agents/scanners/Python serves all project containers (the "one copy of Python serves 10,000 sandboxes" idea from Alex Jones's post).
- Per-project build drops from ~10–20 min to ~1–3 min (only toolchains + setup).
- Disk: 3.2 GB shared once + small deltas per project, instead of N×3.2 GB.
- Reproducible: base is pinned; project layers are tiny.
Security notes
- Pinning the base image (digest) gives supply-chain revocability: bump the base once, rebuild projects → old agents/scanners stop everywhere. This is exactly the "revoke a bad tool once, it stops everywhere" property.
- Keeping the per-project build small shrinks the attack surface that runs with project-specific code.
Files
templates/devcontainer/Dockerfile.tmpl — split into Dockerfile.base.tmpl + slim Dockerfile.tmpl
lib/aidc.sh — aidc::cmd_up/cmd_rebuild should ensure base image presence (docker image inspect aidc-base:...) before compose build
templates/devcontainer/compose.yaml.tmpl — build: args unchanged
Acceptance criteria
docker images shows one aidc-base + small per-project images.
- Fresh
aidc init + aidc up on a new repo takes ≤3 min for a go-only project.
docker system df build cache stops growing by ~3 GB per new project.
Problem
Every project currently builds the entire aidc image from
mcr.microsoft.com/devcontainers/base:ubuntu-24.04up, re-installing the same ~2.5–3 GB of common tooling (base features, apt packages, uv+Python, all 5 coding agents, all scanners) per project, every time a project image is (re)built.Measured: with zero toolchains, the image is already 3.22 GB. 10 projects = ~32 GB of images with ~30 GB of it identical content.
What's shared today
Only the base image itself (
devcontainers/base:ubuntu-24.04, pinned by digest) is shared across project images via Docker layer dedup — because it's pulled once and every project builds on the same base layer. Everything afterFROM(apt, uv, agents, scanners, toolchains) is rebuilt per project. In practice the layer content is mostly identical across projects, so Docker can dedup — but only if the build happens at the same time with the same unpinned installer results;curl | bashagent installers fetch "latest" at build time, so layer digests drift and the build cache (45.5 GB on this host!) grows instead of being shared.Proposal: prebuilt
aidc-baseimageDockerfile.base— everything common: base, apt deps, uv+Python, agents, pmg/vet/rtk, semgrep/gitleaks/trufflehog, clipboard bridge, bootstrap-claude script, PATH wiring. No per-project args exceptAIDC_AGENTS.Dockerfilebecomes:FROM aidc-base:<pinned-version>+ toolchain layer + project-setup.sh. Tiny, fast, and identical across projects.aidc-baseonce (tag:aidc-base:vX.Yor digest-pinned), and haveaidc uppull it if missing — don't rebuild it per project.compose.yaml, build context for the per-project image already exists; just change the Dockerfile to reference the shared base.Result:
Security notes
Files
templates/devcontainer/Dockerfile.tmpl— split intoDockerfile.base.tmpl+ slimDockerfile.tmpllib/aidc.sh—aidc::cmd_up/cmd_rebuildshould ensure base image presence (docker image inspect aidc-base:...) before compose buildtemplates/devcontainer/compose.yaml.tmpl—build: argsunchangedAcceptance criteria
docker imagesshows oneaidc-base+ small per-project images.aidc init+aidc upon a new repo takes ≤3 min for a go-only project.docker system dfbuild cache stops growing by ~3 GB per new project.