File size: 2,882 Bytes
2011737
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
---
description: Docker build cache conventions for CCAI-Vibe-Demo
globs: Dockerfile,docker-compose*.yml,.dockerignore,backend/requirements.txt,frontend/package*.json
alwaysApply: false
---

# Docker build cache conventions

This project uses **BuildKit cache mounts** so `npm ci` and `pip install`
skip the network when only source files change. Don't replace them
with plain `RUN` commands - that reintroduces full re-downloads on every
dependency change.

## Required Dockerfile pattern

The Dockerfile MUST start with the BuildKit syntax directive:

```dockerfile
# syntax=docker/dockerfile:1.7
```

### Frontend stage (root user, node:22-alpine)

```dockerfile
COPY frontend/package.json frontend/package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci
```

### Backend stage (USER user, uid 1000, python:3.12-slim)

```dockerfile
COPY --chown=user backend/requirements.txt ./
RUN --mount=type=cache,target=/home/user/.cache/pip,uid=1000,gid=1000 \
    pip install --user -r requirements.txt
```

The `uid=1000,gid=1000` flags are mandatory because the cache target
sits inside the unprivileged user's home dir. Without them the cache
is created root-owned and pip cannot write to it.

## Don'ts

- **Don't** pass `--no-cache-dir` to `pip install`. It defeats the
  cache mount. The mount lives outside the image, so wheels never
  bloat the final layer either way.
- **Don't** `COPY .` blindly. Layers must be copy-deps-first,
  copy-source-second so a source edit doesn't bust the deps layer.
- **Don't** add new files outside `backend/`, `frontend/` or the
  lockfiles to a `COPY` line without first checking `.dockerignore`.

## Build context (`.dockerignore`)

The ignore list excludes `.git/`, `node_modules/`, `frontend/build/`,
`agent-transcripts/`, `__pycache__`, secrets (`.env*`),
`docker-compose.override.yml`, `backend/tests/`, and editor metadata.
If you need a previously-ignored path inside the image, add a narrow
`!path/to/keep` exception rather than removing the broad ignore.

## Verifying the cache works

After a Dockerfile or deps-file change, run two builds back to back:

```powershell
docker compose build
docker compose build  # second time
```

The second build's `npm ci` / `pip install` step should print a
`CACHED` line (when nothing in the deps file changed) or a
near-instant `=> [internal] load build context` finish (when only
source files changed). If it re-downloads, the cache mount is
mis-configured.

If a build ever errors with `the --mount option requires BuildKit`,
opt in explicitly:

```powershell
$env:DOCKER_BUILDKIT=1; docker compose build
```

## HuggingFace Spaces compatibility

HuggingFace's Docker Space builder honors the `# syntax=` directive
and BuildKit cache mounts. Older builders silently treat the mounts
as no-ops, so this Dockerfile remains forward-compatible with any
plain-Docker environment.