A .dockerignore file is easy to overlook because it does not run inside the container, install packages, expose ports, or start the application. It sits beside the Dockerfile and quietly decides which files Docker is allowed to see during the build.
That decision matters. Before Docker executes most Dockerfile instructions, it prepares a build context: the set of files from your project that are available to the build. If the context includes node_modules, .git, local logs, test output, editor settings, screenshots, cache directories, and .env files, Docker may spend time sending and hashing content the image never needed. Worse, a broad COPY . . instruction may accidentally copy sensitive or irrelevant files into the image.
A good .dockerignore file keeps the build context small, predictable, and safer. It is not just a cleanup preference. It is part of how container builds remain fast and controlled.
The Build Context Comes First
When you run a command such as:
docker build -t my-app .
the final . is the build context. Docker uses that directory as the source of files available to the build. The .dockerignore file is applied to that context before excluded files are sent into the build process.
The flow is:
choose build context
read .dockerignore rules
exclude matching files
send remaining context to builder
run Dockerfile instructions
This order is important. A file ignored by .dockerignore is not available to COPY or ADD. If the Dockerfile says COPY package.json . but .dockerignore excludes package.json, the build cannot copy it. The ignore file is not a suggestion; it changes what the builder can access.
Why It Improves Build Speed
Docker builds often spend surprising time on context preparation. Large dependency folders, generated assets, coverage reports, temporary files, and Git history all add cost. Even if the Dockerfile never copies those files into the final image, the builder may still have to consider them unless they are ignored.
For a Node.js project, sending node_modules can be especially wasteful. The folder may contain thousands of files installed for the host operating system. A container build usually installs dependencies inside the image anyway, often for a different Linux environment.
Ignoring node_modules reduces context size:
node_modules/
npm-debug.log*
yarn-error.log*
pnpm-debug.log*
The effect is not only faster uploads to the builder. A smaller context also means fewer files that can affect cache checks, fewer accidental copies, and less noise when debugging build behavior.
Why It Helps Security
Projects often contain files that should never be available to a container build. Local .env files, private keys, cloud credentials, database dumps, production backups, and developer-specific configuration can all live near source code.
Ignoring those files reduces the chance that a broad copy instruction pulls them into an image:
.env
.env.*
*.pem
*.key
secrets/
backups/
This is not a complete secrets strategy. Secrets should not live casually in project folders, and build systems should use proper secret mechanisms when a build needs temporary credentials. Still, .dockerignore is a useful guardrail. It narrows what Docker can see in the first place.
It Is Not the Same as .gitignore
.dockerignore and .gitignore look similar because both contain path patterns, but they control different systems. .gitignore tells Git which untracked files to ignore. .dockerignore tells Docker which files to exclude from the build context.
A file can be tracked by Git and still excluded from Docker. A file can be ignored by Git and still accidentally sent to Docker if .dockerignore does not exclude it.
For example, a generated dist/ folder may be ignored by Git but included in Docker if the build context allows it. That may be intentional if the Dockerfile copies prebuilt assets. It may be accidental if the Dockerfile builds assets from source.
Treat the files separately. Some patterns overlap, but each one should be written for its own job.
Basic Pattern Examples
Common .dockerignore entries are straightforward:
.git
node_modules/
coverage/
*.log
.env
.DS_Store
.vscode/
.idea/
tmp/
The ! operator can re-include files that were excluded by a broader rule:
*.md
!README.md
That ignores Markdown files except README.md. Use exceptions carefully. If a parent directory is excluded, re-including a child can be unintuitive unless the parent path is still visible to the matcher. Keep rules simple when possible.
Comments can document intent:
# Local dependencies are installed inside the image.
node_modules/
# Do not send local secrets into the build context.
.env
.env.*
A short comment now can prevent a confusing build failure later.
A Node.js Example
A typical Node service might start with:
node_modules/
.npm/
.pnpm-store/
coverage/
dist/
build/
*.log
npm-debug.log*
yarn-error.log*
pnpm-debug.log*
.env
.env.*
.git
.gitignore
.vscode/
.idea/
Whether to ignore dist/ depends on the Dockerfile. If the image builds from TypeScript source, ignore dist/ and let the container build create it. If the image intentionally copies a prebuilt dist/ from CI, do not ignore it for that Dockerfile. The ignore file and Dockerfile need to agree about where build artifacts come from.
A Python Example
Python projects often exclude bytecode, virtual environments, test caches, and local environment files:
__pycache__/
*.py[cod]
.pytest_cache/
.mypy_cache/
.ruff_cache/
.venv/
venv/
env/
htmlcov/
coverage.xml
.env
.env.*
.git
.vscode/
.idea/
Virtual environments belong to the host development environment. The container should install dependencies into its own filesystem using the Dockerfile. Copying a local virtual environment into an image usually creates portability problems and unnecessary size.
Monorepos and Multiple Dockerfiles
Monorepos make .dockerignore more important because the build context can easily include far more than one service needs. If you build from the repository root, Docker may see unrelated packages, test fixtures, documentation, generated apps, and other teams’ files.
One solution is to use a narrower build context when possible:
docker build -f services/api/Dockerfile services/api
Another is to use Dockerfile-specific ignore files. Docker supports ignore files named after the Dockerfile, such as:
Dockerfile
.dockerignore
api.Dockerfile
api.Dockerfile.dockerignore
worker.Dockerfile
worker.Dockerfile.dockerignore
This lets a repository keep different context rules for different images. A test image may need fixtures that a production image should never see. A worker image may need scripts that the API image does not.
Cache Behavior
Docker build cache depends on the files visible to build steps. If irrelevant files are included in the context, changing them can invalidate cache layers or at least add work to context preparation. Ignoring them makes builds more stable.
For example, a Dockerfile often copies dependency manifests before source code:
COPY package.json package-lock.json ./
RUN npm ci
COPY src ./src
This pattern lets Docker reuse the dependency layer when application source changes but package.json does not. A clean .dockerignore supports that strategy by keeping unrelated files out of the context and reducing accidental cache churn.
.dockerignore does not replace careful Dockerfile layering, but it makes caching easier to reason about.
Common Mistakes
The first mistake is ignoring a file the Dockerfile needs. If the build says COPY pyproject.toml ., that file must be present in the context. When Docker reports that a source file cannot be found, check .dockerignore as well as the path.
The second mistake is assuming .gitignore protects Docker builds. It does not. Docker uses .dockerignore.
The third mistake is copying local dependency directories. Host dependencies may be huge, platform-specific, stale, or built with different native libraries than the container expects.
The fourth mistake is leaving secrets visible to the build. Even if the final image does not copy them, reducing access is better than trusting every future Dockerfile change.
The fifth mistake is ignoring generated output without checking the build strategy. Some images build artifacts inside Docker. Others copy artifacts produced by CI. The ignore rules should match the actual workflow.
A Practical Starting Point
For many projects, a useful starting point is:
.git
.gitignore
.env
.env.*
*.pem
*.key
node_modules/
.venv/
venv/
__pycache__/
coverage/
*.log
tmp/
temp/
.DS_Store
.vscode/
.idea/
Then adjust it for the project. Do not blindly paste a universal file. A frontend image, Python worker, Go service, Java application, and monorepo build can have different needs.
How to Review It
Review .dockerignore whenever the Dockerfile changes, the project structure changes, or the build context becomes noticeably slow. Ask three questions:
- What does this image actually need to build?
- What files would be risky if accidentally copied?
- What files change often but should not affect the build?
Those questions keep the ignore file tied to the build rather than treated as forgotten boilerplate.
References
These Docker references are useful for the exact behavior and related build features:
- Docker build context and .dockerignore
- Dockerfile reference
- Docker build cache
- Docker multi-stage builds
- BuildKit documentation
Conclusion
A .dockerignore file controls what Docker receives as build context. That makes it important for build speed, cache stability, image cleanliness, and reducing the chance of exposing local files or secrets.
The best ignore file is not the longest one. It is the one that matches the Dockerfile’s real needs: include what the build requires, exclude what it never should see, and revisit the rules when the build changes.





