Build Errors
Docker layer caching for uv workspace monorepos
Docker layer caching for uv workspace monorepos
Severity: P3 (build ergonomics; correctness unaffected) · Applies to: uv workspace monorepos with Docker images Related: ADR-049 — Container strategy
Problem
In a uv workspace monorepo, a naive Dockerfile runs uv sync after copying all source files. Any change to any .py file invalidates the dependency layer, causing a full re-download of all packages on every build (~60s+).
Symptoms:
- Every
docker buildre-downloads all pip packages - CI builds are slow even for small code changes
- No layer cache hits after the
COPY . .step
Root Cause
Docker layers are invalidated when any file in a COPY command changes. If you copy source code before running uv sync, the dependency install layer is never cached.
Solution
Two-phase uv sync — install third-party deps first (cached), then link workspace packages (fast).
FROM python:3.14-slimCOPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/WORKDIR /app
# Phase 1: Copy ONLY dependency manifests (changes rarely)COPY pyproject.toml uv.lock .python-version ./COPY packages/core/pyproject.toml packages/core/pyproject.tomlCOPY apps/api/pyproject.toml apps/api/pyproject.toml
# Install deps without linking workspace packagesRUN uv sync --package spectral-api --frozen --no-install-workspace --no-dev
# Phase 2: Copy source code (changes often)COPY packages/core/src/ packages/core/src/COPY apps/api/src/ apps/api/src/
# Link workspace packages (fast — just symlinking)RUN uv sync --package spectral-api --frozen --no-devKey flags
| Flag | Purpose |
|---|---|
--no-install-workspace | Skip local package linking; only install remote deps |
--frozen | Use uv.lock exactly; no version negotiation |
--no-dev | Skip dev dependencies for production images |
--package <name> | Only sync deps for one workspace member |
Performance
| Scenario | Without pattern | With pattern |
|---|---|---|
| First build | ~60s | ~60s |
| Source-only change | ~60s | ~2s |
| Dependency change | ~60s | ~60s |
Prevention
- Always separate dependency installation from source code copying in Dockerfiles
- Apply the same principle in CI: install deps in a cached step before copying test code
- This pattern is not in uv docs — document it for your team