.dockerignore Cheatsheet - Docker Build Context Ignore Rules

If you skip .dockerignore, docker build . packs node_modules and .git into the build context, slowing builds and bloating images. This reference covers 5 groups: basic syntax, glob patterns, common ignores, differences from .gitignore, and slimming tips. After writing a template, verify the context size with docker build, and remember a ! negation only re-includes items already ignored. After reading you can ship a safe, lean .dockerignore for your project.

Config Formats·26 commands·Last updated 2026-08-02
.dockerignoredockerBuild ContextconfigIgnore

Basic Syntax 6

node_modules
Ignore a file or dir named node_modules
*.log
Ignore all .log files
tmp/
Only ignore tmp at root (with slash)
**/tmp
Ignore tmp at any level
# comment
Hash starts a comment line
Empty lines are ignored

Glob Patterns 6

*
Match any number of non-separator chars
?
Match a single non-separator char
**
Match any directory level
[abc]
Match any one char in brackets
*.tmp
Ignore all .tmp files
dist/*
Ignore dist's direct children (keep dist itself)

Common Ignores 6

.git
Don't bake the git repo into the build context
node_modules
Dependency dir, usually installed inside the image
.env
Don't leak secrets into the build env
Dockerfile
Ignore itself (rarely needed)
*.md
Ignore docs
.dockerignore
Ignoring itself doesn't affect the current build

vs .gitignore 4

!node_modules/important
Use ! to negate and re-include an ignored item
Dockerfile ignores .gitignore
Dockerfile ignores .gitignore
Build context = docker build .
Build context = docker build .
COPY is still path-limited
COPY is still path-limited

Slimming Tips 4

**/*.pyc
Ignore all compiled caches
.cache/
Ignore local cache dir
coverage/
Ignore coverage reports
tests/
Ignore test dir to shrink context

Command Examples

A sufficient starter template

.git node_modules .env *.log **/*.pyc .cache/ coverage/ tests/

The first four cover version control, dependencies, secrets and logs — the most commonly missed and the most dangerous group. The rest are size reductions: **/*.pyc reaches compiled cache files at every depth, while the trailing slash in .cache/ anchors at the root.

What the wildcard forms actually mean

*.log tmp/ **/tmp dist/*

*.log catches .log files at any depth; tmp/ excludes only the directory at the root; **/tmp excludes every tmp at every depth; dist/* removes only the first level inside dist while keeping the directory itself, so whether the directory must survive decides which form you need.

Negation to re-include: position decides whether it works

node_modules !node_modules/important

The negation must follow the exclusion, because rules match top down and later entries win. The net effect is that node_modules stays out of the context while the important directory inside it is pulled back in.

The boundary with .gitignore: three facts worth separating

# comment .dockerignore

A leading hash marks a comment and blank lines are ignored outright. Ignoring the file itself does not affect the current build, but omitting it leaves you without a record of the rules inside the image. Docker does not read .gitignore; the two files must be maintained separately.

Common Pitfalls

  • .dockerignore only affects what enters the build context; it does not restrict what COPY may target, so it is not part of your Dockerfile path safety.
  • Confirm a path was excluded by an earlier rule before negating it. Negating something never ignored is a no-op and will not pull it in.
  • Once .env enters the context it stays in an image layer, and deleting the file later does not remove that layer. Secret-bearing files must be ignored from the very first build.
  • dist/* keeps the directory itself while dist/** removes it as well. Pick the wrong one and a mount point you intended to keep will disappear.
  • The two ignore files are independent, and copying one to stand in for the other is the most common reason they stop working.

Tips

  • Without .dockerignore, docker build . sends the whole dir to the daemon; node_modules and .git blow up the context.
  • tmp/ with a slash matches only root tmp; tmp without slash matches any level.
  • .dockerignore and .gitignore are independent; they can't share one file.
  • Use ** for cross-level dir matching, e.g. **/__pycache__ ignores all Python caches.

FAQ

Without a .dockerignore, what exactly does docker build . send?

Everything in the current directory, unfiltered. The build context is the whole tree handed to the daemon, and the only exclusions are the entries listed in .dockerignore. So a repository with no ignore rules ships its .git history, node_modules, build output, local logs and even .env — the slowdown is only the first symptom, and the real problem is that secrets end up baked into image layers: deleting the file later does not remove the layer. Start from a template covering three groups, namely dependency directories, version control metadata, and local configuration.

What is the difference between tmp/ and **/tmp?

The trailing separator is what restricts the match to the root. tmp/ with its slash matches only the tmp directory directly under the build context root, whereas tmp without a slash matches a same-named file or directory at any depth. **/tmp states the cross-depth intent explicitly — semantically the same as the bare form — but it reads better in deeply nested projects and makes the intent obvious at a glance. Choose by intent: tmp/ for a scratch directory at the build root, and the slashless or ** form to exclude every same-named directory wherever it appears.

Can one .dockerignore file serve as .gitignore too?

No. The syntaxes resemble each other but the semantics are separate: .gitignore governs version control and .dockerignore governs the build context. Docker never reads .gitignore and git never reads .dockerignore, so sharing one file leaves the other scenario with no exclusions at all. The practical difference also shows up in negation: ! re-includes previously ignored paths, but it can only resurrect something a rule already excluded — with no prior matching rule, the negation is a no-op.

Why does my ! negation fail to re-include a file?

Almost always because the negation sits too early, or because no preceding rule ever matched the path. Negation undoes an existing exclusion rather than explicitly including something: if .env was never excluded to begin with, !.env is a redundant line, and if a later exclusion rule covers the path again, the negation is overridden. Rules match top to bottom with later entries winning, so a negated path must come after the rule that excluded it. Remember too that negation only affects what enters the context — it does not relax what COPY may target.

How do I confirm the ignore rules actually took effect?

Watch the context-transfer step of the build output, and cross-check the image size through its layer history. With many rules the usual failure is a pattern that looks right but anchors at the wrong depth, so the file still ships; rebuilding after each edit is far more reliable than reasoning from memory. For size, docker history shows per-layer volume and identifies which layer introduced the bulk, which you then translate back into a new rule. A solid starting set covers .git, dependency directories, local configuration, test and coverage output, and cache directories; add from there.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Aug 2, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us