Skip to content

perf: optimize Docker build from ~10min to ~2min#503

Draft
Bl3f wants to merge 1 commit intomainfrom
cursor/docker-build-speed-optimization-7df8
Draft

perf: optimize Docker build from ~10min to ~2min#503
Bl3f wants to merge 1 commit intomainfrom
cursor/docker-build-speed-optimization-7df8

Conversation

@Bl3f
Copy link
Contributor

@Bl3f Bl3f commented Mar 20, 2026

Summary

Optimizes the Docker build pipeline across pr-preview.yml, docker.yml, and the Dockerfile to reduce build times from ~10 minutes to ~2 minutes.

Bottleneck analysis (from CI logs)

Step Before After (est.) Optimization
GHA cache export (mode=max) 335s ~30s Switch to registry cache
chown -R nao:nao /app 35s 0s COPY --chown=nao:nao
Runtime apt-get + nodesource + bun 36s ~5s Copy Node.js/Bun from base stage
Redundant bun install (2 stages) 25s 0s Shared deps stage
pip install uv (×2 stages) 12s 0s Pre-built uv binary via COPY
Exporting layers 54s ~30s Smaller layer footprint

Changes

Dockerfile

  • Consolidated deps stage: merged frontend-builder and backend-builder dependency installs into a single deps stage. Frontend builder inherits from it; runtime copies node_modules from it. Eliminates a redundant ~25s bun install.
  • COPY --chown=nao:nao: all COPY instructions in the runtime stage now set ownership inline, replacing the expensive recursive chown -R nao:nao /app /var/log/supervisor (35s → 0s).
  • Copy Node.js/Bun from base stage: instead of running the nodesource.com setup script + npm install -g bun in the runtime stage (~36s), copies the binaries from the already-built node:24-slim base stage and creates symlinks.
  • Pre-built uv binary: COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv replaces pip install uv in both the python-builder and runtime stages (saves ~6s each).
  • BuildKit cache mounts: added --mount=type=cache for bun and uv package caches (speeds up local development rebuilds).

pr-preview.yml

  • Switched from type=gha,mode=max to type=registry cache stored in GHCR alongside the preview image.
  • Added the previous PR image as an additional cache source for faster incremental builds.

docker.yml

  • Switched from type=gha,mode=max to type=registry cache stored in GHCR (ghcr.io/<repo>/build-cache), with per-platform cache tags.
  • Added GHCR login step and packages: write permission for cache push access.

Testing

  • ✅ Full Docker build completed locally with the new Dockerfile
  • ✅ All runtime binaries verified: Node.js v24.14.0, Bun v1.3.11, npm 11.9.0, uv 0.10.12, Python 3.12.13
  • ✅ File ownership correct (nao:nao) on all app files
  • ✅ Frontend dist built successfully, backend source present, ripgrep binary present
  • npm run lint passes
Open in Web Open in Cursor 

Key optimizations:

1. Switch from GHA cache to registry cache (saves ~5min)
   - GHA cache mode=max uploads every layer via a slow separate API
   - Registry cache uses the standard registry push protocol, much faster
   - Added GHCR login to docker.yml for cache storage
   - PR preview also uses previous PR image as cache source

2. Eliminate expensive recursive chown -R (saves ~35s)
   - Replace final 'chown -R nao:nao /app' with COPY --chown=nao:nao
   - All COPY instructions in the runtime stage now set ownership inline
   - Only chown the empty top-level directories, not the full tree

3. Copy Node.js/Bun from base stage instead of apt-get install (saves ~30s)
   - Avoids slow nodesource.com setup script + npm install -g bun
   - Copies binaries directly from the node:24-slim base stage

4. Consolidate two bun install stages into one shared deps stage
   - Frontend and backend were doing redundant bun installs
   - Single deps stage with --ignore-scripts + ripgrep postinstall
   - Frontend builder inherits from deps, backend copies node_modules

5. Use pre-built uv binary instead of pip install (saves ~12s)
   - COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
   - Applied to both python-builder and runtime stages

6. Add BuildKit cache mounts for bun and uv package caches
   - Speeds up local development rebuilds

Co-authored-by: Christophe Blefari <christophe.blefari@gmail.com>
@github-actions
Copy link
Contributor

🚀 Preview Deployment

URL https://pr-503-314a052.preview.getnao.io
Commit 314a052

⚠️ No LLM API keys configured - you'll see the API key setup flow when trying to chat.


Preview will be automatically removed when this PR is closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants