.dockerignore & Build Context
Summary
- The build context is the file tree sent to the builder;
.dockerignoreexcludes files from it to speed builds and avoid leaking secrets. - Insight:
COPY . .copies whatever is in the context, so a fat context means a fat, slow, and sometimes insecure image. - Key Concepts: build context,
.dockerignorepatterns, transfer cost, cache invalidation, secret leakage. - When to Use: Every repository. A
.dockerignoreis the cheapest, highest-leverage build hygiene you can add. - Limitations:
.dockerignorefilters the context only; it does not remove files already committed inside a base image.
Recipe
Add a .dockerignore next to the Dockerfile and exclude version control, dependencies, secrets, and build output.
.git
node_modules
dist
*.env
**/*.log
Dockerfile
.dockerignore
With this in place, docker build . transfers far less data and COPY . . no longer drags in .git or local secrets.
Working Example
A realistic .dockerignore for a Node service, plus a Dockerfile that copies a clean context.
# .dockerignore
.git
.gitignore
node_modules
npm-debug.log
dist
coverage
.env
.env.*
*.pem
.vscode
.idea
README.md
# syntax=docker/dockerfile:1
FROM node:22-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
USER node
CMD ["node", "server.js"]Because node_modules and dist are excluded, the copied source stays small, and the container rebuilds dependencies from the lockfile rather than shipping the host's platform-specific modules.
Confirm what actually goes into the context by watching the transfer at the start of the build.
docker build -t app:1.0 .
# "transferring context: 1.2MB" - if this is hundreds of MB, tighten .dockerignoreDeep Dive
What the build context is
When you run docker build <path>, BuildKit collects <path> as the context and makes it available to COPY and ADD. Files outside the context are simply unreachable during the build.
A large context costs on every build. It must be scanned and transferred to the builder, and it enlarges the checksum work that keys COPY cache entries.
Pattern syntax
.dockerignore uses Go filepath match rules, similar to but not identical to .gitignore. * matches within a path segment, ** matches across segments, and a leading ! re-includes a previously excluded path.
# exclude everything, then allow specific files
*
!package.json
!package-lock.json
!src/**
This allowlist style is stricter and safer than a denylist for repositories that accumulate stray files.
Cache implications
COPY . . invalidates its layer whenever any included file changes. Excluding editor state, logs, and test artifacts stops unrelated changes from busting the cache.
Copy narrow paths where you can. COPY src ./src invalidates only when src changes, which is tighter than copying the whole tree.
Contexts in Compose and CI
Compose v2 builds each service from its own build.context, so a shared root context can quietly send unrelated services' files to every build. Point each service at the narrowest directory it needs.
In CI, a shallow or filtered checkout keeps the context lean before Docker even sees it. Combine that with .dockerignore so both the checkout and the transfer stay small on every pipeline run.
Security angle
Sending .git to the builder exposes full history, and COPY . . can bake it or a stray .env into a layer where it persists even if a later step deletes it. Excluding these at the context boundary is the reliable fix.
Gotchas
.dockerignore must sit at the root of the build context, not next to a nested Dockerfile in a different directory. BuildKit reads the one at the context root.
Excluding a file from the context does not remove it from a base image. If a secret is already in a layer you inherit, .dockerignore cannot help.
COPY . . after a weak .dockerignore still copies secrets. Treat the ignore file as a security control, and prefer an allowlist for sensitive repos.
Ignoring the Dockerfile itself is fine and common; the builder reads it separately and does not need it inside the context.
A missing .dockerignore in a monorepo can send gigabytes to the builder. Always check the "transferring context" size on the first build.
Alternatives
Copying only explicit paths (COPY src ./src, COPY package.json ./) instead of COPY . . limits exposure even without a .dockerignore, but it is easy to forget a needed file.
Building from a clean checkout or an artifact tarball avoids stray local files entirely, at the cost of an extra pipeline step.
Remote or Git-URL build contexts pull a defined tree rather than your working directory, which sidesteps local cruft but reduces local iteration speed.
FAQs
Does .dockerignore use the same syntax as .gitignore? Close, but not identical. It follows Go's filepath match rules, so test patterns rather than assuming parity.
Where must .dockerignore live? At the root of the build context you pass to docker build, not necessarily beside the Dockerfile.
Will ignoring node_modules break my build? No, as long as you install dependencies in the image with npm ci. Host modules are often built for the wrong platform anyway.
Can I ignore everything and allow a few files? Yes. Use * to exclude all, then !path lines to re-include what you need.
Does a big context slow builds even with good layer caching? Yes. The context is transferred and checksummed before caching helps, so size still costs time.
Is sending .git really a problem? Yes. It exposes full history and can be baked into a layer. Exclude it in every repository.
How do I see what the context actually contains? Watch the "transferring context" size on the first build, or build with --progress=plain to see the transfer step in detail.
Does .dockerignore affect COPY --from stages? No. It filters only the build context sent from your machine, not files copied from other stages or images.
Related
- Dockerfile Basics
- How a Dockerfile Becomes an Image
- BuildKit & Cache Mounts
- Multi-Stage Builds
- Dockerfiles Best Practices
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).