Skip to content

Image size: build a prebuilt shared aidc-base image so projects don't rebuild ~3GB of common tooling #7

Description

@ai-anant

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

  1. 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.
  2. 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.
  3. 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.shaidc::cmd_up/cmd_rebuild should ensure base image presence (docker image inspect aidc-base:...) before compose build
  • templates/devcontainer/compose.yaml.tmplbuild: 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions