.dockerignore reference
Last reviewed on 2026-05-02
Pattern syntax, precedence rules, common mistakes, and how it interacts with the build context, COPY, and ADD.
The .dockerignore file sits in the root of the build context and tells the Docker CLI which files and directories should not be sent to the daemon when a build is started. It is one of the highest-leverage files in any Docker project: a careful .dockerignore can shave gigabytes off the build context, prevent secrets from being copied into images, and keep the layer cache stable across machines.
Where it goes
The file is named exactly .dockerignore (with the leading dot) and lives in the root of the build context — usually next to the Dockerfile at the top of your repository. When you invoke docker build ., the CLI looks for a .dockerignore in the current directory; if you pass an explicit context path, it looks there.
Recent BuildKit versions also support a per-Dockerfile ignore file: if you build with docker build -f services/api/Dockerfile services/api, BuildKit will check for services/api/Dockerfile.dockerignore first and only fall back to a root .dockerignore if the per-Dockerfile file is absent. This lets a monorepo give each service its own ignore rules.
Syntax
Each non-empty, non-comment line is a pattern. Comments begin with #. Patterns use the same matching rules as Go's filepath.Match, with three additions: a leading ! negates a pattern, a leading ** matches any number of directories, and patterns are matched against paths relative to the build context root.
| Pattern | Matches |
|---|---|
node_modules | The directory node_modules at the root of the context, recursively. |
**/node_modules | Any node_modules directory at any depth. |
*.log | Any file ending in .log at the root of the context. |
**/*.log | Any file ending in .log at any depth. |
build/ | Equivalent to build; trailing slash is allowed but not required. |
!important.log | Re-include important.log even if a previous rule excluded it. |
secrets/** | Everything inside secrets/ at any depth. |
Precedence and order of evaluation
Rules are evaluated in order, and the last matching rule wins. This is the single most common source of confusion. To exclude everything except a specific subdirectory, you exclude broadly first and then re-include with !:
# exclude everything
*
# but include these specific files
!Dockerfile
!package.json
!package-lock.json
!src/**
Re-inclusion does not work across directories that are themselves excluded. If you exclude src with the bare pattern src, you cannot re-include src/index.js, because the parent has already been pruned. Use the directory-glob form src/** when you want to allow exceptions inside it.
What it actually skips
A common misconception is that .dockerignore only filters what gets COPYed. It does more than that: it filters the build context itself, before the daemon ever sees it. That has three consequences:
- Build-time speed. A 5 GB
node_modulesdirectory not sent to the daemon is 5 GB of upload that doesn't happen, locally or remotely. - Cache invalidation. If a file changes but is excluded by
.dockerignore, it cannot bust the cache for any layer, because BuildKit never sees it. - Secret hygiene. Files excluded from the context cannot be accidentally copied into the image by a sloppy
COPY . ..
Worked example: a typical Node.js project
# Dependencies and build artefacts
node_modules
**/node_modules
dist
build
coverage
.next
.cache
# Logs and editor noise
*.log
*.swp
.DS_Store
.idea
.vscode
# Secrets and local config
.env
.env.*
!.env.example
secrets/**
*.pem
*.key
# Git and CI
.git
.gitignore
.github
Note the deliberate use of !.env.example: the rule .env.* would otherwise sweep it up. The last-match-wins rule lets you check in a sample env file without exposing real ones.
Common mistakes
- No
.dockerignoreat all. The default is "send everything," including.git,node_modules, and any local.envfiles. Always have one. - Excluding
.gitwhen you actually need it. If your build usesgit describeor embeds the commit SHA, excluding.gitwill silently break it. - Re-include rules that don't fire. If
!important.logisn't taking effect, check for an earlier rule that excluded the parent directory or a later rule that re-excluded it. - Forgetting
**. A barenode_modulesonly matches at the root. Nestednode_modulesdirectories from monorepo packages will sneak through unless you also list**/node_modules. - Excluding files referenced by an earlier
COPYstage. In a multi-stage build, a file you exclude is gone from every stage. If a build stage needs it but the runtime stage shouldn't, copy selectively in the runtime stage instead.
Checklist before committing your .dockerignore
- Run
docker build .and look at the "transferring context" line — it should be measured in MB, not GB. - Inspect the resulting image with
docker run --rm -it <image> ls -la /ordocker historyand confirm no.git, no editor backups, and no environment files appear. - Search the image for known secret filenames:
docker run --rm <image> find / -name '.env*' -o -name '*.pem' 2>/dev/null. - Commit the
.dockerignore. Don't gitignore it.
Related
- Build context reference — how the build context is assembled.
- COPY reference — how files in the context become layers.
- ADD reference — when ADD's extra behaviour matters.
- Layer caching reference — why a quiet build context keeps caches warm.
- Securing Docker builds tutorial — using
.dockerignoreas part of a defence-in-depth strategy.