← Debugging Guides

阅读中文版本

Docker build context too large — Speed Up Builds with .dockerignore

Diagnose oversized Docker build contexts by measuring directory size and transfer output, then exclude node_modules, .git, and artifacts with .dockerignore.

beginnerdebuggingdockerbuild

Symptoms

  • docker build prints Sending build context to Docker daemon 2.4GB
  • Every build takes minutes before the first Dockerfile step runs
  • CI disk or network usage spikes during builds
  • Build cache is invalidated when unrelated test files change

Possible causes

  • The build is run from the repository root and includes node_modules, .git, or virtualenvs
  • There is no .dockerignore file, or it does not cover build outputs
  • Large datasets or logs are stored under the build directory
  • The Dockerfile copies a broad path such as COPY . . without excluding artifacts

Diagnosis steps

Measure the directory being sent

du -sh . && du -sh -- * .[!.]* 2>/dev/null | sort -h | tail -15

Interpretation

The largest entries are what the daemon tars up. node_modules, .git, dist, and data directories commonly dominate.

Watch the context size reported by the builder

docker build --progress=plain -t app . 2>&1 | head -20

Interpretation

The Sending build context line is the actual payload. Anything above a few hundred megabytes slows every build regardless of layer caching.

Estimate the tarball size

tar -cf - --exclude-vcs . 2>/dev/null | wc -c

Interpretation

This approximates what the client sends before compression. Compare it with the reported context to confirm the same directories are involved.

Check for an effective ignore file

ls -la .dockerignore 2>/dev/null && docker build --no-cache -t app . 2>&1 | grep 'build context'

Interpretation

A missing file or one without node_modules and .git leaves the context large even when the Dockerfile is correct.

Root cause

The Docker client archives the entire build context and sends it to the daemon or BuildKit before evaluating the Dockerfile. Ignoring files in the Dockerfile does not help because the transfer already happened. A repository with dependencies, VCS history, and test artifacts therefore pays that transfer on every build and can invalidate cache when an unrelated file changes.

Fix

Add a .dockerignore that excludes node_modules, .git, dist, coverage, logs, local data, and secrets, then build from the smallest directory that contains the Dockerfile and required sources. For monorepos, use BuildKit named contexts or a dedicated build stage so only the relevant package is sent.

Risk note

Ignoring a file that the build actually needs causes COPY failures or a broken image. .dockerignore patterns differ from .gitignore, especially for negation and directory matching. Secrets excluded from the context must still never be baked into layers.

Verify the fix

Rebuild and confirm the Sending build context line drops to the expected size, the first Dockerfile step starts quickly, and the resulting image still passes its smoke test. Change an ignored test file and verify the build cache is not invalidated.

Context is transferred before the Dockerfile matters

Sending build context to Docker daemon  2.147GB

That line is the client archiving everything in the build directory and shipping it to the builder. A COPY or .dockerignore decision happens later; the transfer cost is already paid.

Find the weight

du -sh . && du -sh -- * .[!.]* 2>/dev/null | sort -h | tail -15
tar -cf - --exclude-vcs . 2>/dev/null | wc -c

In a typical Node repository, node_modules and .git account for most of the payload. Python projects add .venv and __pycache__; data teams add notebooks and fixtures.

Write a real .dockerignore

.git
.gitignore
node_modules
dist
coverage
playwright-report
test-results
*.log
.env*
data/

Then build from the app directory or use a named context:

docker build --progress=plain -t app .

If the Dockerfile needs files from outside the current directory, prefer docker buildx build --build-context shared=../shared . over sending the entire monorepo.

Verify

The context line should fall to a few megabytes, and the first build step should start immediately. Confirm the image still starts and serves traffic, then touch an ignored test file and rebuild. If the cache still invalidates, inspect the COPY instructions: a broad COPY . . will invalidate on any included file even when the context is small.